|
// ---------------------------------------------------------------------------
// Copyright (C) 2006 Microsoft Corporation All Rights Reserved
// ---------------------------------------------------------------------------
#define CODE_ANALYSIS
using System.CodeDom;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Text;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.Activities.Common;
namespace System.Workflow.Activities.Rules
{
#region ExpressionInfo
// Public base class (which just holds the Type of the expression).
public class RuleExpressionInfo
{
private Type expressionType;
public RuleExpressionInfo(Type expressionType)
{
this.expressionType = expressionType;
}
public Type ExpressionType
{
get { return expressionType; }
}
}
// Internal derivation for CodeMethodInvokeExpression
internal class RuleMethodInvokeExpressionInfo : RuleExpressionInfo
{
private MethodInfo methodInfo;
private bool needsParamsExpansion;
internal RuleMethodInvokeExpressionInfo(MethodInfo mi, bool needsParamsExpansion)
: base(mi.ReturnType)
{
this.methodInfo = mi;
this.needsParamsExpansion = needsParamsExpansion;
}
internal MethodInfo MethodInfo
{
get { return methodInfo; }
}
internal bool NeedsParamsExpansion
{
get { return needsParamsExpansion; }
}
}
// Internal derivation for CodeBinaryExpression
internal class RuleBinaryExpressionInfo : RuleExpressionInfo
{
private Type leftType;
private Type rightType;
private MethodInfo methodInfo;
// no overridden method needed
internal RuleBinaryExpressionInfo(Type lhsType, Type rhsType, Type resultType)
: base(resultType)
{
this.leftType = lhsType;
this.rightType = rhsType;
}
// overridden method found
internal RuleBinaryExpressionInfo(Type lhsType, Type rhsType, MethodInfo mi)
: base(mi.ReturnType)
{
this.leftType = lhsType;
this.rightType = rhsType;
this.methodInfo = mi;
}
internal Type LeftType
{
get { return leftType; }
}
internal Type RightType
{
get { return rightType; }
}
internal MethodInfo MethodInfo
{
get { return methodInfo; }
}
}
// Internal derivation for CodeFieldReferenceExpression
internal class RuleFieldExpressionInfo : RuleExpressionInfo
{
private FieldInfo fieldInfo;
internal RuleFieldExpressionInfo(FieldInfo fi)
: base(fi.FieldType)
{
fieldInfo = fi;
}
internal FieldInfo FieldInfo
{
get { return fieldInfo; }
}
}
// Internal derivation for CodePropertyReferenceExpression
internal class RulePropertyExpressionInfo : RuleExpressionInfo
{
private PropertyInfo propertyInfo;
private bool needsParamsExpansion;
// Note that the type pi.PropertyType may differ from the "exprType" argument if this
// property is a Bind.
internal RulePropertyExpressionInfo(PropertyInfo pi, Type exprType, bool needsParamsExpansion)
: base(exprType)
{
this.propertyInfo = pi;
this.needsParamsExpansion = needsParamsExpansion;
}
internal PropertyInfo PropertyInfo
{
get { return propertyInfo; }
}
internal bool NeedsParamsExpansion
{
get { return needsParamsExpansion; }
}
}
// Internal derivation for CodeMethodInvokeExpression
internal class RuleConstructorExpressionInfo : RuleExpressionInfo
{
private ConstructorInfo constructorInfo;
private bool needsParamsExpansion;
internal RuleConstructorExpressionInfo(ConstructorInfo ci, bool needsParamsExpansion)
: base(ci.DeclaringType)
{
this.constructorInfo = ci;
this.needsParamsExpansion = needsParamsExpansion;
}
internal ConstructorInfo ConstructorInfo
{
get { return constructorInfo; }
}
internal bool NeedsParamsExpansion
{
get { return needsParamsExpansion; }
}
}
internal class ExtensionMethodInfo : MethodInfo
{
MethodInfo actualMethod;
int actualParameterLength;
ParameterInfo[] expectedParameters;
Type assumedDeclaringType;
bool hasOutOrRefParameters = false;
public ExtensionMethodInfo(MethodInfo method, ParameterInfo[] actualParameters)
: base()
{
Debug.Assert(method.IsStatic, "Expected static method as an extension method");
actualMethod = method;
// modify parameters
actualParameterLength = actualParameters.Length;
if (actualParameterLength < 2)
expectedParameters = new ParameterInfo[0];
else
{
expectedParameters = new ParameterInfo[actualParameterLength - 1];
Array.Copy(actualParameters, 1, expectedParameters, 0, actualParameterLength - 1);
foreach (ParameterInfo pi in expectedParameters)
{
if (pi.ParameterType.IsByRef)
hasOutOrRefParameters = true;
}
}
// get the type we pretend this method is on (which happens to be the first actual parameter)
assumedDeclaringType = actualParameters[0].ParameterType;
}
public override MethodInfo GetBaseDefinition()
{
return actualMethod.GetBaseDefinition();
}
public override ICustomAttributeProvider ReturnTypeCustomAttributes
{
get { return actualMethod.ReturnTypeCustomAttributes; }
}
public override MethodAttributes Attributes
{
get { return actualMethod.Attributes & ~MethodAttributes.Static; }
}
public override MethodImplAttributes GetMethodImplementationFlags()
{
return actualMethod.GetMethodImplementationFlags();
}
public override ParameterInfo[] GetParameters()
{
return expectedParameters;
}
public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)
{
object[] actualParameters = new object[actualParameterLength];
if (actualParameterLength > 1)
Array.Copy(parameters, 0, actualParameters, 1, actualParameterLength - 1);
if (obj == null)
actualParameters[0] = null;
else
actualParameters[0] = Executor.AdjustType(obj.GetType(), obj, assumedDeclaringType);
object result = actualMethod.Invoke(null, invokeAttr, binder, actualParameters, culture);
// may be out/ref parameters, so copy back the results
if (hasOutOrRefParameters)
Array.Copy(actualParameters, 1, parameters, 0, actualParameterLength - 1);
return result;
}
public override RuntimeMethodHandle MethodHandle
{
get { return actualMethod.MethodHandle; }
}
public override Type DeclaringType
{
get { return actualMethod.DeclaringType; }
}
public Type AssumedDeclaringType
{
get { return assumedDeclaringType; }
}
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
{
return actualMethod.GetCustomAttributes(attributeType, inherit);
}
public override object[] GetCustomAttributes(bool inherit)
{
return actualMethod.GetCustomAttributes(inherit);
}
public override bool IsDefined(Type attributeType, bool inherit)
{
return actualMethod.IsDefined(attributeType, inherit);
}
public override string Name
{
get { return actualMethod.Name; }
}
public override Type ReflectedType
{
get { return actualMethod.ReflectedType; }
}
public override Type ReturnType
{
get { return actualMethod.ReturnType; }
}
}
internal class SimpleParameterInfo : ParameterInfo
{
// only thing we look at is ParameterType, so no need to override anything else
Type parameterType;
public SimpleParameterInfo(ParameterInfo parameter)
: base()
{
parameterType = typeof(Nullable<>).MakeGenericType(parameter.ParameterType);
}
public SimpleParameterInfo(Type parameter)
: base()
{
parameterType = parameter;
}
public override Type ParameterType
{
get
{
return parameterType;
}
}
}
internal abstract class BaseMethodInfo : MethodInfo
{
protected MethodInfo actualMethod;
protected ParameterInfo[] expectedParameters;
protected Type resultType;
public BaseMethodInfo(MethodInfo method)
: base()
{
Debug.Assert(method.IsStatic, "Expected static method as an lifted method");
actualMethod = method;
resultType = method.ReturnType;
expectedParameters = method.GetParameters();
}
public override MethodInfo GetBaseDefinition()
{
return actualMethod.GetBaseDefinition();
}
public override ICustomAttributeProvider ReturnTypeCustomAttributes
{
get { return actualMethod.ReturnTypeCustomAttributes; }
}
public override MethodAttributes Attributes
{
get { return actualMethod.Attributes & ~MethodAttributes.Static; }
}
public override MethodImplAttributes GetMethodImplementationFlags()
{
return actualMethod.GetMethodImplementationFlags();
}
public override ParameterInfo[] GetParameters()
{
return expectedParameters;
}
public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)
{
throw new NotImplementedException();
}
public override RuntimeMethodHandle MethodHandle
{
get { return actualMethod.MethodHandle; }
}
public override Type DeclaringType
{
get { return actualMethod.DeclaringType; }
}
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
{
return actualMethod.GetCustomAttributes(attributeType, inherit);
}
public override object[] GetCustomAttributes(bool inherit)
{
return actualMethod.GetCustomAttributes(inherit);
}
public override bool IsDefined(Type attributeType, bool inherit)
{
return actualMethod.IsDefined(attributeType, inherit);
}
public override string Name
{
get { return actualMethod.Name; }
}
public override Type ReflectedType
{
get { return actualMethod.ReflectedType; }
}
public override Type ReturnType
{
get { return resultType; }
}
public override bool Equals(object obj)
{
BaseMethodInfo other = obj as BaseMethodInfo;
if ((other == null)
|| (actualMethod != other.actualMethod)
|| (resultType != other.resultType)
|| (expectedParameters.Length != other.expectedParameters.Length))
return false;
for (int i = 0; i < expectedParameters.Length; ++i)
if (expectedParameters[i].ParameterType != other.expectedParameters[i].ParameterType)
return false;
return true;
}
public override int GetHashCode()
{
int result = actualMethod.GetHashCode() ^ resultType.GetHashCode();
for (int i = 0; i < expectedParameters.Length; ++i)
result ^= expectedParameters[i].GetHashCode();
return result;
}
}
internal class LiftedConversionMethodInfo : BaseMethodInfo
{
public LiftedConversionMethodInfo(MethodInfo method)
: base(method)
{
Debug.Assert(expectedParameters.Length == 1, "not 1 parameters");
// modify result
resultType = typeof(Nullable<>).MakeGenericType(method.ReturnType);
// modify parameter (exactly 1)
ParameterInfo[] actualParameters = method.GetParameters();
expectedParameters = new ParameterInfo[1];
expectedParameters[0] = new SimpleParameterInfo(actualParameters[0]);
}
public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)
{
// null in, then result is null
if (parameters[0] == null)
return Activator.CreateInstance(resultType);
// invoke the conversion from S -> T
object result = actualMethod.Invoke(null, invokeAttr, binder, parameters, culture);
// return a T?
return Executor.AdjustType(actualMethod.ReturnType, result, resultType);
}
}
internal class LiftedArithmeticOperatorMethodInfo : BaseMethodInfo
{
public LiftedArithmeticOperatorMethodInfo(MethodInfo method)
: base(method)
{
Debug.Assert(expectedParameters.Length == 2, "not 2 parameters");
// modify parameters (exactly 2, both need to be lifted)
ParameterInfo[] actualParameters = method.GetParameters();
expectedParameters = new ParameterInfo[2];
expectedParameters[0] = new SimpleParameterInfo(actualParameters[0]);
expectedParameters[1] = new SimpleParameterInfo(actualParameters[1]);
// modify result
resultType = typeof(Nullable<>).MakeGenericType(method.ReturnType);
}
public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)
{
// null in, then result is null
if (parameters[0] == null)
return null;
if (parameters[1] == null)
return null;
// apply the underlying operator
object result = actualMethod.Invoke(null, invokeAttr, binder, parameters, culture);
// return a T?
return Executor.AdjustType(actualMethod.ReturnType, result, resultType);
}
}
internal class LiftedEqualityOperatorMethodInfo : BaseMethodInfo
{
public LiftedEqualityOperatorMethodInfo(MethodInfo method)
: base(method)
{
Debug.Assert(method.ReturnType == typeof(bool), "not a bool result");
Debug.Assert(expectedParameters.Length == 2, "not 2 parameters");
// modify parameters (exactly 2, both need to be lifted)
ParameterInfo[] actualParameters = method.GetParameters();
expectedParameters = new ParameterInfo[2];
expectedParameters[0] = new SimpleParameterInfo(actualParameters[0]);
expectedParameters[1] = new SimpleParameterInfo(actualParameters[1]);
// set the result type
resultType = typeof(bool);
}
public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)
{
// null == null is true, null == something else is false, else call method
if (parameters[0] == null)
return (parameters[1] == null);
else if (parameters[1] == null)
return false;
// invoke the actual comparison (parameters are unwrapped)
return actualMethod.Invoke(null, invokeAttr, binder, parameters, culture);
}
}
internal class LiftedRelationalOperatorMethodInfo : BaseMethodInfo
{
public LiftedRelationalOperatorMethodInfo(MethodInfo method)
: base(method)
{
Debug.Assert(method.ReturnType == typeof(bool), "not a bool result");
Debug.Assert(expectedParameters.Length == 2, "not 2 parameters");
// modify parameters (exactly 2, both need to be lifted)
ParameterInfo[] actualParameters = method.GetParameters();
expectedParameters = new ParameterInfo[2];
expectedParameters[0] = new SimpleParameterInfo(actualParameters[0]);
expectedParameters[1] = new SimpleParameterInfo(actualParameters[1]);
// set the result type
resultType = typeof(bool);
}
public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)
{
// if either parameter is null, then result is false
if (parameters[0] == null)
return false;
if (parameters[1] == null)
return false;
// invoke the actual comparison (parameters are unwrapped)
return actualMethod.Invoke(null, invokeAttr, binder, parameters, culture);
}
}
internal class EnumOperationMethodInfo : MethodInfo
{
CodeBinaryOperatorType op;
ParameterInfo[] expectedParameters;
Type resultType; // may be nullable, enum, or value type
bool resultIsNullable; // true if resultType is nullable
Type lhsBaseType; // non-Nullable, may be enum
Type rhsBaseType;
Type resultBaseType;
Type lhsRootType; // underlying type (int, long, ushort, etc)
Type rhsRootType;
Type resultRootType;
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
public EnumOperationMethodInfo(Type lhs, CodeBinaryOperatorType operation, Type rhs, bool isZero)
{
// only 5 arithmetic cases (U = underlying type of E):
// E = E + U
// E = U + E
// U = E - E
// E = E - U
// E = U - E
// plus 5 comparison cases
// E == E
// E < E
// E <= E
// E > E
// E >= E
// either E can be nullable
op = operation;
// parameters are easy -- they are the same as the type passed in
expectedParameters = new ParameterInfo[2];
expectedParameters[0] = new SimpleParameterInfo(lhs);
expectedParameters[1] = new SimpleParameterInfo(rhs);
// compute return type (depends on type of operation)
// start by getting the types without Nullable<>
bool lhsNullable = ConditionHelper.IsNullableValueType(lhs);
bool rhsNullable = ConditionHelper.IsNullableValueType(rhs);
lhsBaseType = (lhsNullable) ? Nullable.GetUnderlyingType(lhs) : lhs;
rhsBaseType = (rhsNullable) ? Nullable.GetUnderlyingType(rhs) : rhs;
// determine the underlying types for both sides
if (lhsBaseType.IsEnum)
lhsRootType = EnumHelper.GetUnderlyingType(lhsBaseType);
else
lhsRootType = lhsBaseType;
if (rhsBaseType.IsEnum)
rhsRootType = EnumHelper.GetUnderlyingType(rhsBaseType);
else
rhsRootType = rhsBaseType;
switch (op)
{
case CodeBinaryOperatorType.Add:
// add always produces an enum, except enum + enum
if ((lhsBaseType.IsEnum) && (rhs.IsEnum))
resultBaseType = lhsRootType;
else if (lhsBaseType.IsEnum)
resultBaseType = lhsBaseType;
else
resultBaseType = rhsBaseType;
// if either side is nullable, result is nullable
resultIsNullable = (lhsNullable || rhsNullable);
resultType = (resultIsNullable) ? typeof(Nullable<>).MakeGenericType(resultBaseType) : resultBaseType;
break;
case CodeBinaryOperatorType.Subtract:
// subtract can be an enum or the underlying type
if (rhsBaseType.IsEnum && lhsBaseType.IsEnum)
{
resultRootType = rhsRootType;
resultBaseType = rhsRootType;
}
else if (lhsBaseType.IsEnum)
{
// special case for E - 0
// if 0 is the underlying type, then use E - U
// if not the underlying type, then 0 becomes E, use E - E
resultRootType = lhsRootType;
if (isZero && rhsBaseType != lhsRootType)
resultBaseType = lhsRootType;
else
resultBaseType = lhsBaseType;
}
else // rhsType.IsEnum
{
// special case for 0 - E
// in all cases 0 becomes E, use E - E
resultRootType = rhsRootType;
if (isZero)
resultBaseType = rhsRootType;
else
resultBaseType = rhsBaseType;
}
resultIsNullable = (lhsNullable || rhsNullable);
resultType = (resultIsNullable) ? typeof(Nullable<>).MakeGenericType(resultBaseType) : resultBaseType;
break;
case CodeBinaryOperatorType.ValueEquality:
case CodeBinaryOperatorType.LessThan:
case CodeBinaryOperatorType.LessThanOrEqual:
case CodeBinaryOperatorType.GreaterThan:
case CodeBinaryOperatorType.GreaterThanOrEqual:
resultType = typeof(bool);
break;
}
}
public override MethodInfo GetBaseDefinition()
{
return null;
}
public override ICustomAttributeProvider ReturnTypeCustomAttributes
{
get { return null; }
}
public override MethodAttributes Attributes
{
get { return MethodAttributes.Static; }
}
public override MethodImplAttributes GetMethodImplementationFlags()
{
return MethodImplAttributes.Runtime;
}
public override ParameterInfo[] GetParameters()
{
return expectedParameters;
}
[SuppressMessage("Microsoft.Performance", "CA1803:AvoidCostlyCallsWherePossible")]
public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)
{
// we should get passed in 2 values that correspond to the parameter types
object result;
ArithmeticLiteral leftArithmetic, rightArithmetic;
Literal leftLiteral, rightLiteral;
// for design-time types we couldn't find the underlying type, so do it now
if (lhsRootType == null)
lhsRootType = Enum.GetUnderlyingType(lhsBaseType);
if (rhsRootType == null)
rhsRootType = Enum.GetUnderlyingType(rhsBaseType);
switch (op)
{
case CodeBinaryOperatorType.Add:
// if either is null, then the result is null
if ((parameters[0] == null) || (parameters[1] == null))
return null;
leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsRootType, parameters[0]);
rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsRootType, parameters[1]);
result = leftArithmetic.Add(rightArithmetic);
result = Executor.AdjustType(result.GetType(), result, resultBaseType);
if (resultIsNullable)
result = Activator.CreateInstance(resultType, result);
return result;
case CodeBinaryOperatorType.Subtract:
// if either is null, then the result is null
if ((parameters[0] == null) || (parameters[1] == null))
return null;
leftArithmetic = ArithmeticLiteral.MakeLiteral(resultRootType,
Executor.AdjustType(lhsRootType, parameters[0], resultRootType));
rightArithmetic = ArithmeticLiteral.MakeLiteral(resultRootType,
Executor.AdjustType(rhsRootType, parameters[1], resultRootType));
result = leftArithmetic.Subtract(rightArithmetic);
result = Executor.AdjustType(result.GetType(), result, resultBaseType);
if (resultIsNullable)
result = Activator.CreateInstance(resultType, result);
return result;
case CodeBinaryOperatorType.ValueEquality:
leftLiteral = Literal.MakeLiteral(lhsRootType, parameters[0]);
rightLiteral = Literal.MakeLiteral(rhsRootType, parameters[1]);
return leftLiteral.Equal(rightLiteral);
case CodeBinaryOperatorType.LessThan:
leftLiteral = Literal.MakeLiteral(lhsRootType, parameters[0]);
rightLiteral = Literal.MakeLiteral(rhsRootType, parameters[1]);
return leftLiteral.LessThan(rightLiteral);
case CodeBinaryOperatorType.LessThanOrEqual:
leftLiteral = Literal.MakeLiteral(lhsRootType, parameters[0]);
rightLiteral = Literal.MakeLiteral(rhsRootType, parameters[1]);
return leftLiteral.LessThanOrEqual(rightLiteral);
case CodeBinaryOperatorType.GreaterThan:
leftLiteral = Literal.MakeLiteral(lhsRootType, parameters[0]);
rightLiteral = Literal.MakeLiteral(rhsRootType, parameters[1]);
return leftLiteral.GreaterThan(rightLiteral);
case CodeBinaryOperatorType.GreaterThanOrEqual:
leftLiteral = Literal.MakeLiteral(lhsRootType, parameters[0]);
rightLiteral = Literal.MakeLiteral(rhsRootType, parameters[1]);
return leftLiteral.GreaterThanOrEqual(rightLiteral);
}
string message = string.Format(CultureInfo.CurrentCulture, Messages.BinaryOpNotSupported, op.ToString());
throw new RuleEvaluationException(message);
}
public override RuntimeMethodHandle MethodHandle
{
get { return new RuntimeMethodHandle(); }
}
public override Type DeclaringType
{
get { return typeof(Enum); }
}
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
{
return new object[0];
}
public override object[] GetCustomAttributes(bool inherit)
{
return new object[0];
}
public override bool IsDefined(Type attributeType, bool inherit)
{
return true;
}
public override string Name
{
get { return "op_Enum"; }
}
public override Type ReflectedType
{
get { return resultType; }
}
public override Type ReturnType
{
get { return resultType; }
}
}
#endregion
#region SimpleRunTimeTypeProvider
internal class SimpleRunTimeTypeProvider : ITypeProvider
{
private Assembly root;
private List<Assembly> references;
internal SimpleRunTimeTypeProvider(Assembly startingAssembly)
{
root = startingAssembly;
}
public Type GetType(string name)
{
return GetType(name, false);
}
public Type GetType(string name, bool throwOnError)
{
// is the type available in the main workflow assembly?
Type type = root.GetType(name, throwOnError, false);
if (type != null)
return type;
// now try mscorlib or this assembly
// (or if the name is an assembly qualified name)
type = Type.GetType(name, throwOnError, false);
if (type != null)
return type;
// no luck so far, so try all referenced assemblies
foreach (Assembly a in ReferencedAssemblies)
{
type = a.GetType(name, throwOnError, false);
if (type != null)
return type;
}
// keep going by trying all loaded assemblies
Assembly[] loaded = AppDomain.CurrentDomain.GetAssemblies();
for (int i = 0; i < loaded.Length; ++i)
{
type = loaded[i].GetType(name, throwOnError, false);
if (type != null)
return type;
}
return null;
}
public Type[] GetTypes()
{
List<Type> types = new List<Type>();
try
{
types.AddRange(root.GetTypes());
}
catch (ReflectionTypeLoadException e)
{
// problems loading all the types, take what we can get
foreach (Type type in e.Types)
if (type != null)
types.Add(type);
}
foreach (Assembly a in ReferencedAssemblies)
{
try
{
types.AddRange(a.GetTypes());
}
catch (ReflectionTypeLoadException e)
{
// problems loading all the types, take what we can get
foreach (Type type in e.Types)
if (type != null)
types.Add(type);
}
}
return types.ToArray();
}
public Assembly LocalAssembly
{
get { return root; }
}
public ICollection<Assembly> ReferencedAssemblies
{
get
{
// references is created on demand, does not include root
if (references == null)
{
List<Assembly> list = new List<Assembly>();
foreach (AssemblyName a in root.GetReferencedAssemblies())
{
list.Add(Assembly.Load(a));
}
references = list;
}
return references;
}
}
public IDictionary<object, Exception> TypeLoadErrors
{
get
{
// we never use this method, so add use of EventHandlers to keep compiler happy
TypesChanged.Invoke(this, null);
TypeLoadErrorsChanged.Invoke(this, null);
return null;
}
}
public event EventHandler TypesChanged;
public event EventHandler TypeLoadErrorsChanged;
}
#endregion
#region RuleValidation
public class RuleValidation
{
private Type thisType;
private ITypeProvider typeProvider;
private ValidationErrorCollection errors = new ValidationErrorCollection();
private Dictionary<string, Type> typesUsed = new Dictionary<string, Type>(16);
private Dictionary<string, Type> typesUsedAuthorized;
private Stack<CodeExpression> activeParentNodes = new Stack<CodeExpression>();
private Dictionary<CodeExpression, RuleExpressionInfo> expressionInfoMap = new Dictionary<CodeExpression, RuleExpressionInfo>();
private Dictionary<CodeTypeReference, Type> typeRefMap = new Dictionary<CodeTypeReference, Type>();
private bool checkStaticType;
private IList<AuthorizedType> authorizedTypes;
private static readonly Type voidType = typeof(void);
private static string voidTypeName = voidType.AssemblyQualifiedName;
#region Constructors
// Validate at design time.
public RuleValidation(Activity activity, ITypeProvider typeProvider, bool checkStaticType)
{
if (activity == null)
throw new ArgumentNullException("activity");
if (typeProvider == null)
throw new ArgumentNullException("typeProvider");
this.thisType = ConditionHelper.GetContextType(typeProvider, activity);
this.typeProvider = typeProvider;
this.checkStaticType = checkStaticType;
if (checkStaticType)
{
Debug.Assert(WorkflowCompilationContext.Current != null, "Can't have checkTypes set to true without a context in scope");
this.authorizedTypes = WorkflowCompilationContext.Current.GetAuthorizedTypes();
this.typesUsedAuthorized = new Dictionary<string, Type>();
this.typesUsedAuthorized.Add(voidTypeName, voidType);
}
}
// Validate at runtime when we have the actual subject instance. This is
// mostly for conditions used in activities like IfElse.
internal RuleValidation(object thisObject)
{
if (thisObject == null)
throw new ArgumentNullException("thisObject");
this.thisType = thisObject.GetType();
this.typeProvider = new SimpleRunTimeTypeProvider(this.thisType.Assembly);
}
// Validate at runtime when we have just the type. This is mostly for rules.
public RuleValidation(Type thisType, ITypeProvider typeProvider)
{
if (thisType == null)
throw new ArgumentNullException("thisType");
this.thisType = thisType;
this.typeProvider = (typeProvider != null) ? typeProvider : new SimpleRunTimeTypeProvider(this.thisType.Assembly);
}
#endregion
#region Internal validation methods
internal bool ValidateConditionExpression(CodeExpression expression)
{
if (expression == null)
throw new ArgumentNullException("expression");
// Run the validation pass.
RuleExpressionInfo exprInfo = RuleExpressionWalker.Validate(this, expression, false);
if (exprInfo == null)
return false;
Type resultType = exprInfo.ExpressionType;
if (!IsValidBooleanResult(resultType))
{
// not a boolean, so complain unless another error may have caused this problem
if (resultType != null || Errors.Count == 0)
{
string message = Messages.ConditionMustBeBoolean;
ValidationError error = new ValidationError(message, ErrorNumbers.Error_ConditionMustBeBoolean);
error.UserData[RuleUserDataKeys.ErrorObject] = expression;
Errors.Add(error);
}
}
return Errors.Count == 0;
}
internal static bool IsValidBooleanResult(Type type)
{
return ((type == typeof(bool))
|| (type == typeof(bool?))
|| (ImplicitConversion(type, typeof(bool))));
}
internal static bool IsPrivate(MethodInfo methodInfo)
{
return methodInfo.IsPrivate
|| methodInfo.IsFamily
|| methodInfo.IsFamilyOrAssembly
|| methodInfo.IsFamilyAndAssembly;
}
internal static bool IsPrivate(FieldInfo fieldInfo)
{
return fieldInfo.IsPrivate
|| fieldInfo.IsFamily
|| fieldInfo.IsFamilyOrAssembly
|| fieldInfo.IsFamilyAndAssembly;
}
internal static bool IsInternal(MethodInfo methodInfo)
{
return methodInfo.IsAssembly
|| methodInfo.IsFamilyAndAssembly;
}
internal static bool IsInternal(FieldInfo fieldInfo)
{
return fieldInfo.IsAssembly
|| fieldInfo.IsFamilyAndAssembly;
}
#endregion
#region Miscellaneous public properties & methods
public Type ThisType
{
get { return thisType; }
}
internal ITypeProvider GetTypeProvider()
{
return typeProvider;
}
public ValidationErrorCollection Errors
{
get { return errors; }
}
internal bool AllowInternalMembers(Type type)
{
return type.Assembly == thisType.Assembly;
}
internal void AddError(ValidationError error)
{
this.Errors.Add(error);
}
public bool PushParentExpression(CodeExpression newParent)
{
if (newParent == null)
throw new ArgumentNullException("newParent");
if (activeParentNodes.Contains(newParent))
{
string message = string.Format(CultureInfo.CurrentCulture, Messages.CyclicalExpression);
ValidationError error = new ValidationError(message, ErrorNumbers.Error_CyclicalExpression);
error.UserData[RuleUserDataKeys.ErrorObject] = newParent;
Errors.Add(error);
return false;
}
activeParentNodes.Push(newParent);
return true;
}
public void PopParentExpression()
{
activeParentNodes.Pop();
}
// Get the ExpressionInfo associated with a given CodeExpression
public RuleExpressionInfo ExpressionInfo(CodeExpression expression)
{
if (expression == null)
throw new ArgumentNullException("expression");
RuleExpressionInfo exprInfo = null;
expressionInfoMap.TryGetValue(expression, out exprInfo);
return exprInfo;
}
#endregion
#region CodeDom Expression Validation methods
internal RuleExpressionInfo ValidateSubexpression(CodeExpression expr, RuleExpressionInternal ruleExpr, bool isWritten)
{
Debug.Assert(ruleExpr != null, "Validation::ValidateSubexpression - IRuleExpression is null");
Debug.Assert(expr != null, "Validation::ValidateSubexpression - CodeExpression is null");
RuleExpressionInfo exprInfo = ruleExpr.Validate(expr, this, isWritten);
if (exprInfo != null)
{
// Add the CodeExpression object to the info map. We don't want to add the IRuleExpression guy
// as the key, since it might likely be just a tearoff wrapper.
expressionInfoMap[expr] = exprInfo;
}
return exprInfo;
}
internal static bool TypesAreAssignable(Type rhsType, Type lhsType, CodeExpression rhsExpression, out ValidationError error)
{
// determine if rhsType can be implicitly converted to lhsType,
// following the rules in C# specification section 6.1,
// plus support for Nullable<T>
// all but 6.1.7 handled as a standard implicit conversion
if (StandardImplicitConversion(rhsType, lhsType, rhsExpression, out error))
return true;
if (error != null)
return false;
// no standard implicit conversion works, see if user specified one
// from section 6.4.3, start by determining what types to check
// as we find each type, add the list of implicit conversions available
if (FindImplicitConversion(rhsType, lhsType, out error) == null)
return false;
return true;
}
internal static bool ExplicitConversionSpecified(Type fromType, Type toType, out ValidationError error)
{
// determine if fromType can be implicitly converted to toType,
// following the rules in C# specification section 6.2
// start by seeing if there is a standard implicit conversion
if (StandardImplicitConversion(fromType, toType, null, out error))
return true;
if (error != null)
return false;
// explicit numeric conversions
// also handles Enum conversions, since GetTypeCode returns the underlying type
if (fromType.IsValueType && toType.IsValueType && IsExplicitNumericConversion(fromType, toType))
return true;
// explicit reference conversions
// this looks like the inverse of implicit conversions
ValidationError dummyError; // so we don't return an error
if (StandardImplicitConversion(toType, fromType, null, out dummyError))
return true;
// include interface checks
if (toType.IsInterface)
{
// from any class-type S to any interface-type T, provided S is not sealed and provided S does not implement T.
// latter part should be handled by implicit conversion, so we are ok as long as class is not sealed
if ((fromType.IsClass) && (!fromType.IsSealed))
return true;
// from any interface-type S to any interface-type T, provided S is not derived from T.
// again, if S derived from T, handled by implicit conversion above
if (fromType.IsInterface)
return true;
}
if (fromType.IsInterface)
{
// from any interface-type S to any class-type T, provided T is not sealed or provided T implements S.
if ((toType.IsClass) && ((!toType.IsSealed) || (InterfaceMatch(toType.GetInterfaces(), fromType))))
return true;
}
// no look for user-defined conversions
// from section 6.4.4, start by determining what types to check
// as we find each type, add the list of implicit conversions available
if (FindExplicitConversion(fromType, toType, out error) == null)
return false;
return true;
}
private static bool InterfaceMatch(Type[] types, Type fromType)
{
foreach (Type t in types)
{
if (t == fromType)
return true;
}
return false;
}
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
internal static MethodInfo FindImplicitConversion(Type fromType, Type toType, out ValidationError error)
{
List<MethodInfo> candidates = new List<MethodInfo>();
bool fromIsNullable = ConditionHelper.IsNullableValueType(fromType);
bool toIsNullable = ConditionHelper.IsNullableValueType(toType);
Type fromType0 = (fromIsNullable) ? Nullable.GetUnderlyingType(fromType) : fromType;
Type toType0 = (toIsNullable) ? Nullable.GetUnderlyingType(toType) : toType;
if (fromType0.IsClass)
{
AddImplicitConversions(fromType0, fromType, toType, candidates);
Type baseType = fromType0.BaseType;
while ((baseType != null) && (baseType != typeof(object)))
{
AddImplicitConversions(baseType, fromType, toType, candidates);
baseType = baseType.BaseType;
}
}
else if (IsStruct(fromType0))
{
AddImplicitConversions(fromType0, fromType, toType, candidates);
}
if ((toType0.IsClass) || (IsStruct(toType0)))
{
AddImplicitConversions(toType0, fromType, toType, candidates);
}
// if both types are nullable, add the lifted operators
if (fromIsNullable && toIsNullable)
{
// start by finding all the conversion operators from S0 -> T0
List<MethodInfo> liftedCandidates = new List<MethodInfo>();
if (fromType0.IsClass)
{
AddImplicitConversions(fromType0, fromType0, toType0, liftedCandidates);
Type baseType = fromType0.BaseType;
while ((baseType != null) && (baseType != typeof(object)))
{
AddImplicitConversions(baseType, fromType0, toType0, liftedCandidates);
baseType = baseType.BaseType;
}
}
else if (IsStruct(fromType0))
{
AddImplicitConversions(fromType0, fromType0, toType0, liftedCandidates);
}
if ((toType0.IsClass) || (IsStruct(toType0)))
{
AddImplicitConversions(toType0, fromType0, toType0, liftedCandidates);
}
// add them all to the candidates list as lifted methods (which wraps them appropriately)
foreach (MethodInfo mi in liftedCandidates)
{
// only lift candidates that convert from a non-nullable value type
// to a non-nullable value type
ParameterInfo[] parameters = mi.GetParameters();
if (ConditionHelper.IsNonNullableValueType(mi.ReturnType) && ConditionHelper.IsNonNullableValueType(parameters[0].ParameterType))
candidates.Add(new LiftedConversionMethodInfo(mi));
}
}
if (candidates.Count == 0)
{
// no overrides, so must be false
string message = string.Format(CultureInfo.CurrentCulture,
Messages.NoConversion,
RuleDecompiler.DecompileType(fromType),
RuleDecompiler.DecompileType(toType));
error = new ValidationError(message, ErrorNumbers.Error_OperandTypesIncompatible);
return null;
}
// find the most specific source type
ValidationError dummyError; // so we don't return an error
Type sx = candidates[0].GetParameters()[0].ParameterType;
if (sx != fromType)
{
for (int i = 1; i < candidates.Count; ++i)
{
Type testType = candidates[i].GetParameters()[0].ParameterType;
if (testType == fromType)
{
// we have a match with the source type, so that's the correct answer
sx = fromType;
break;
}
if (StandardImplicitConversion(testType, sx, null, out dummyError))
sx = testType;
}
}
// find the most specific target type
Type tx = candidates[0].ReturnType;
if (tx != toType)
{
for (int i = 1; i < candidates.Count; ++i)
{
Type testType = candidates[i].ReturnType;
if (testType == toType)
{
// we have a match with the target type, so that's the correct answer
tx = toType;
break;
}
if (StandardImplicitConversion(tx, testType, null, out dummyError))
tx = testType;
}
}
// see how many candidates convert from sx to tx, ignoring lifted methods
int numMatches = 0;
int position = 0;
for (int i = 0; i < candidates.Count; ++i)
{
if ((candidates[i].ReturnType == tx) &&
(candidates[i].GetParameters()[0].ParameterType == sx) &&
(!(candidates[i] is LiftedConversionMethodInfo)))
{
position = i;
++numMatches;
}
}
if (numMatches == 1)
{
// found what we are looking for
error = null;
return candidates[position];
}
// now check for lifted conversions
if ((toIsNullable) && (numMatches == 0))
{
if (fromIsNullable)
{
for (int i = 0; i < candidates.Count; ++i)
{
if ((candidates[i].ReturnType == tx) &&
(candidates[i].GetParameters()[0].ParameterType == sx) &&
(candidates[i] is LiftedConversionMethodInfo))
{
position = i;
++numMatches;
}
}
if (numMatches == 1)
{
// found what we are looking for
error = null;
return candidates[position];
}
}
else
{
// we are doing a conversion T? = S, so a conversion from S -> T is valid
MethodInfo result = FindImplicitConversion(fromType, toType0, out error);
if (result != null)
{
error = null;
// return it as a lifted method so the wrapping to T? is done
return new LiftedConversionMethodInfo(result);
}
}
}
// no exact matches, so it's an error
string message2 = string.Format(CultureInfo.CurrentCulture,
Messages.AmbiguousConversion,
RuleDecompiler.DecompileType(fromType),
RuleDecompiler.DecompileType(toType));
error = new ValidationError(message2, ErrorNumbers.Error_OperandTypesIncompatible);
return null;
}
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
internal static MethodInfo FindExplicitConversion(Type fromType, Type toType, out ValidationError error)
{
List<MethodInfo> candidates = new List<MethodInfo>();
ValidationError dummyError; // don't return transient errors
bool fromIsNullable = ConditionHelper.IsNullableValueType(fromType);
bool toIsNullable = ConditionHelper.IsNullableValueType(toType);
Type fromType0 = (fromIsNullable) ? Nullable.GetUnderlyingType(fromType) : fromType;
Type toType0 = (toIsNullable) ? Nullable.GetUnderlyingType(toType) : toType;
if (fromType0.IsClass)
{
AddExplicitConversions(fromType0, fromType, toType, candidates);
Type baseType = fromType0.BaseType;
while ((baseType != null) && (baseType != typeof(object)))
{
AddExplicitConversions(baseType, fromType, toType, candidates);
baseType = baseType.BaseType;
}
}
else if (IsStruct(fromType0))
{
AddExplicitConversions(fromType0, fromType, toType, candidates);
}
if (toType0.IsClass)
{
AddExplicitConversions(toType0, fromType, toType, candidates);
Type baseType = toType0.BaseType;
while ((baseType != null) && (baseType != typeof(object)))
{
AddExplicitConversions(baseType, fromType, toType, candidates);
baseType = baseType.BaseType;
}
}
else if (IsStruct(toType0))
{
AddExplicitConversions(toType0, fromType, toType, candidates);
}
// if both types are nullable, add the lifted operators
if (fromIsNullable && toIsNullable)
{
// start by finding all the conversion operators from S0 -> T0
List<MethodInfo> liftedCandidates = new List<MethodInfo>();
if (fromType0.IsClass)
{
AddExplicitConversions(fromType0, fromType0, toType0, liftedCandidates);
Type baseType = fromType0.BaseType;
while ((baseType != null) && (baseType != typeof(object)))
{
AddExplicitConversions(baseType, fromType0, toType0, liftedCandidates);
baseType = baseType.BaseType;
}
}
else if (IsStruct(fromType0))
{
AddExplicitConversions(fromType0, fromType0, toType0, liftedCandidates);
}
if (toType0.IsClass)
{
AddExplicitConversions(toType0, fromType0, toType0, liftedCandidates);
Type baseType = toType0.BaseType;
while ((baseType != null) && (baseType != typeof(object)))
{
AddExplicitConversions(baseType, fromType0, toType0, liftedCandidates);
baseType = baseType.BaseType;
}
}
else if (IsStruct(toType0))
{
AddExplicitConversions(toType0, fromType0, toType0, liftedCandidates);
}
// add them all to the candidates list as lifted methods (which wraps them appropriately)
foreach (MethodInfo mi in liftedCandidates)
candidates.Add(new LiftedConversionMethodInfo(mi));
}
if (candidates.Count == 0)
{
// no overrides, so must be false
string message = string.Format(CultureInfo.CurrentCulture,
Messages.NoConversion,
RuleDecompiler.DecompileType(fromType),
RuleDecompiler.DecompileType(toType));
error = new ValidationError(message, ErrorNumbers.Error_OperandTypesIncompatible);
return null;
}
// find the most specific source type
// if any are s, s is the answer
Type sx = null;
for (int i = 0; i < candidates.Count; ++i)
{
Type testType = candidates[i].GetParameters()[0].ParameterType;
if (testType == fromType)
{
// we have a match with the source type, so that's the correct answer
sx = fromType;
break;
}
}
// if no match, find the most encompassed type if the type encompasses s
if (sx == null)
{
for (int i = 0; i < candidates.Count; ++i)
{
Type testType = candidates[i].GetParameters()[0].ParameterType;
if (StandardImplicitConversion(fromType, testType, null, out dummyError))
{
if (sx == null)
sx = testType;
else if (StandardImplicitConversion(testType, sx, null, out dummyError))
sx = testType;
}
}
}
// still no match, find most encompassing type
if (sx == null)
{
for (int i = 0; i < candidates.Count; ++i)
{
Type testType = candidates[i].GetParameters()[0].ParameterType;
if (StandardImplicitConversion(testType, fromType, null, out dummyError))
{
if (sx == null)
sx = testType;
else if (StandardImplicitConversion(sx, testType, null, out dummyError))
sx = testType;
}
}
}
// find the most specific target type
// if any are t, t is the answer
Type tx = null;
for (int i = 0; i < candidates.Count; ++i)
{
Type testType = candidates[i].ReturnType;
if (testType == toType)
{
// we have a match with the target type, so that's the correct answer
tx = toType;
break;
}
}
// if no match, find the most encompassed type if the type encompasses s
if (tx == null)
{
for (int i = 0; i < candidates.Count; ++i)
{
Type testType = candidates[i].ReturnType;
if (StandardImplicitConversion(testType, toType, null, out dummyError))
{
if (tx == null)
tx = testType;
else if (StandardImplicitConversion(tx, testType, null, out dummyError))
tx = testType;
}
}
}
// still no match, find most encompassing type
if (tx == null)
{
for (int i = 0; i < candidates.Count; ++i)
{
Type testType = candidates[i].ReturnType;
if (StandardImplicitConversion(toType, testType, null, out dummyError))
{
if (tx == null)
tx = testType;
else if (StandardImplicitConversion(testType, tx, null, out dummyError))
tx = testType;
}
}
}
// see how many candidates convert from sx to tx, ignoring lifted methods
int numMatches = 0;
int position = 0;
for (int i = 0; i < candidates.Count; ++i)
{
if ((candidates[i].ReturnType == tx) &&
(candidates[i].GetParameters()[0].ParameterType == sx) &&
(!(candidates[i] is LiftedConversionMethodInfo)))
{
position = i;
++numMatches;
}
}
if (numMatches == 1)
{
// found what we are looking for
error = null;
return candidates[position];
}
// now check for lifted conversions
if ((toIsNullable) && (numMatches == 0))
{
if (fromIsNullable)
{
for (int i = 0; i < candidates.Count; ++i)
{
if ((candidates[i].ReturnType == tx) &&
(candidates[i].GetParameters()[0].ParameterType == sx) &&
(candidates[i] is LiftedConversionMethodInfo))
{
position = i;
++numMatches;
}
}
if (numMatches == 1)
{
// found what we are looking for
error = null;
return candidates[position];
}
}
else
{
// we are doing a conversion T? = S, so a conversion from S -> T is valid
MethodInfo result = FindExplicitConversion(fromType, toType0, out error);
if (result != null)
{
error = null;
// return it as a lifted method so the wrapping to T? is done
return new LiftedConversionMethodInfo(result);
}
}
}
// no exact matches, so it's an error
string message2 = string.Format(CultureInfo.CurrentCulture,
Messages.AmbiguousConversion,
RuleDecompiler.DecompileType(fromType),
RuleDecompiler.DecompileType(toType));
error = new ValidationError(message2, ErrorNumbers.Error_OperandTypesIncompatible);
return null;
}
private static bool IsStruct(Type type)
{
return ((type.IsValueType) && (!type.IsPrimitive));
}
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
private static bool IsExplicitNumericConversion(Type sourceType, Type testType)
{
// includes the implicit conversions as well
// unwrap nullables
TypeCode sourceTypeCode = (ConditionHelper.IsNullableValueType(sourceType))
? Type.GetTypeCode(sourceType.GetGenericArguments()[0])
: Type.GetTypeCode(sourceType);
TypeCode testTypeCode = (ConditionHelper.IsNullableValueType(testType))
? Type.GetTypeCode(testType.GetGenericArguments()[0])
: Type.GetTypeCode(testType);
switch (sourceTypeCode)
{
case TypeCode.SByte:
switch (testTypeCode)
{
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
case TypeCode.Char:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.Byte:
switch (testTypeCode)
{
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Char:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.Int16:
switch (testTypeCode)
{
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
case TypeCode.Char:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.UInt16:
switch (testTypeCode)
{
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Char:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.Int32:
switch (testTypeCode)
{
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.UInt64:
case TypeCode.Char:
case TypeCode.Int64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.UInt32:
switch (testTypeCode)
{
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Char:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.Int64:
switch (testTypeCode)
{
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Char:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.UInt64:
switch (testTypeCode)
{
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Char:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.Char:
switch (testTypeCode)
{
case TypeCode.Char:
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.Single:
switch (testTypeCode)
{
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Char:
case TypeCode.Single:
case TypeCode.Decimal:
case TypeCode.Double:
return true;
}
return false;
case TypeCode.Double:
switch (testTypeCode)
{
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Char:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
case TypeCode.Decimal:
switch (testTypeCode)
{
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Char:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
return false;
}
return false;
}
internal static bool ImplicitConversion(Type fromType, Type toType)
{
ValidationError error;
// is there a standard conversion we can use
if (StandardImplicitConversion(fromType, toType, null, out error))
return true;
// no standard one, did the user provide one?
return (FindImplicitConversion(fromType, toType, out error) != null);
}
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
internal static bool StandardImplicitConversion(Type rhsType, Type lhsType, CodeExpression rhsExpression, out ValidationError error)
{
error = null;
// 6.1.1 identity conversion
if (rhsType == lhsType)
{
// Easy special case... they're the same type.
return true;
}
// 6.1.4 (h) from the null type to any reference-type
if (rhsType == typeof(NullLiteral))
{
// Special case if the RHS is 'null'; just make sure the LHS type can be assigned a null value.
if (ConditionHelper.IsNonNullableValueType(lhsType))
{
string message = string.Format(CultureInfo.CurrentCulture, Messages.AssignNotAllowed, Messages.NullValue, RuleDecompiler.DecompileType(lhsType));
error = new ValidationError(message, ErrorNumbers.Error_OperandTypesIncompatible);
return false;
}
return true;
}
// check for nullables
bool lhsIsNullable = ConditionHelper.IsNullableValueType(lhsType);
bool rhsIsNullable = ConditionHelper.IsNullableValueType(rhsType);
if (rhsIsNullable)
{
if (!lhsIsNullable)
{
// We had T1 = T2?, which is not valid for any T1 or T2, unless T1 is object
return (lhsType == typeof(object));
}
rhsType = Nullable.GetUnderlyingType(rhsType);
}
if (lhsIsNullable)
lhsType = Nullable.GetUnderlyingType(lhsType);
if (lhsType == rhsType)
{
// We had T? = T, which is valid.
return true;
}
// handle rest of 6.1.4
if (TypeProvider.IsAssignable(lhsType, rhsType))
{
// They are assignable, which will handle inheritance and trivial up-casting.
return true;
}
// 6.1.3 implicit enumeration conversions
if (lhsType.IsEnum)
{
// right-hand side can be decimal-integer-literal 0
CodePrimitiveExpression primitive = rhsExpression as CodePrimitiveExpression;
if ((primitive == null) || (primitive.Value == null))
{
// not a constant
return false;
}
switch (Type.GetTypeCode(primitive.Value.GetType()))
{
case TypeCode.SByte:
return ((sbyte)primitive.Value == 0);
case TypeCode.Byte:
return ((byte)primitive.Value == 0);
case TypeCode.Int16:
return ((short)primitive.Value == 0);
case TypeCode.UInt16:
return ((ushort)primitive.Value == 0);
case TypeCode.Int32:
return ((int)primitive.Value == 0);
case TypeCode.UInt32:
return ((uint)primitive.Value == 0);
case TypeCode.Int64:
return ((long)primitive.Value == 0);
case TypeCode.UInt64:
return ((ulong)primitive.Value == 0);
case TypeCode.Char:
return ((char)primitive.Value == 0);
}
return false;
}
if (rhsType.IsEnum)
{
// don't treat enums as numbers
return false;
}
// 6.1.2 implicit numeric conversions
// 6.1.6 implicit constant expression conversions
// not assignable, but the assignment might still be valid for
// value types if a free conversion is available.
TypeCode lhsTypeCode = Type.GetTypeCode(lhsType);
TypeCode rhsTypeCode = Type.GetTypeCode(rhsType);
switch (lhsTypeCode)
{
case TypeCode.Decimal:
switch (rhsTypeCode)
{
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Decimal:
case TypeCode.Char:
return true;
}
return false;
case TypeCode.Double:
switch (rhsTypeCode)
{
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Char:
return true;
}
return false;
case TypeCode.Single:
switch (rhsTypeCode)
{
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Char:
return true;
}
return false;
case TypeCode.Char:
switch (rhsTypeCode)
{
case TypeCode.Char:
return true;
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
// Maybe, if the value is in range.
return CheckValueRange(rhsExpression, lhsType, out error);
}
return false;
case TypeCode.SByte:
switch (rhsTypeCode)
{
case TypeCode.SByte:
return true;
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Char:
// Maybe, if the value is in range.
return CheckValueRange(rhsExpression, lhsType, out error);
}
return false;
case TypeCode.Byte:
switch (rhsTypeCode)
{
case TypeCode.Byte:
return true;
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Char:
// Maybe, if the value is in range.
return CheckValueRange(rhsExpression, lhsType, out error);
}
return false;
case TypeCode.Int16:
switch (rhsTypeCode)
{
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
return true;
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Char:
// Maybe, if the value is in range.
return CheckValueRange(rhsExpression, lhsType, out error);
}
return false;
case TypeCode.Int32:
switch (rhsTypeCode)
{
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.Char:
return true;
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
// Maybe, if the value is in range.
return CheckValueRange(rhsExpression, lhsType, out error);
}
return false;
case TypeCode.Int64:
switch (rhsTypeCode)
{
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.Char:
return true;
case TypeCode.UInt64:
// Maybe, if the value is in range.
return CheckValueRange(rhsExpression, lhsType, out error);
}
return false;
case TypeCode.UInt16:
switch (rhsTypeCode)
{
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.Char:
return true;
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
// Maybe, if the value is in range.
return CheckValueRange(rhsExpression, lhsType, out error);
}
return false;
case TypeCode.UInt32:
switch (rhsTypeCode)
{
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.Char:
return true;
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.UInt64:
// Maybe, if the value is in range.
return CheckValueRange(rhsExpression, lhsType, out error);
}
return false;
case TypeCode.UInt64:
switch (rhsTypeCode)
{
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
case TypeCode.Char:
return true;
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
// Maybe, if the value is in range.
return CheckValueRange(rhsExpression, lhsType, out error);
}
return false;
default:
// It wasn't a numeric type, it was some other kind of value type (e.g., bool,
// DateTime, etc). There will be no conversions.
return false;
}
}
private static void AddImplicitConversions(Type t, Type source, Type target, List<MethodInfo> methods)
{
// append the list of methods that match the name specified
// s is the source type, so the parameter must encompass it
// t is the target type, so it must encompass the result
MethodInfo[] possible = t.GetMethods(BindingFlags.Static | BindingFlags.Public);
foreach (MethodInfo mi in possible)
{
if ((mi.Name == "op_Implicit") && (mi.GetParameters().Length == 1))
{
Type sourceType = mi.GetParameters()[0].ParameterType;
Type targetType = mi.ReturnType;
ValidationError error;
if (StandardImplicitConversion(source, sourceType, null, out error) &&
StandardImplicitConversion(targetType, target, null, out error))
{
if (!methods.Contains(mi))
methods.Add(mi);
}
}
}
}
private static void AddExplicitConversions(Type t, Type source, Type target, List<MethodInfo> methods)
{
// append the list of methods that match the name specified
// s is the source type, so the parameter must encompass it
// t is the target type, so it must encompass the result
MethodInfo[] possible = t.GetMethods(BindingFlags.Static | BindingFlags.Public);
foreach (MethodInfo mi in possible)
{
if (((mi.Name == "op_Implicit") || (mi.Name == "op_Explicit")) && (mi.GetParameters().Length == 1))
{
Type sourceType = mi.GetParameters()[0].ParameterType;
Type targetType = mi.ReturnType;
ValidationError error;
if ((StandardImplicitConversion(source, sourceType, null, out error) || StandardImplicitConversion(sourceType, source, null, out error))
&& (StandardImplicitConversion(target, targetType, null, out error) || StandardImplicitConversion(targetType, target, null, out error)))
{
if (!methods.Contains(mi))
methods.Add(mi);
}
}
}
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
private static bool CheckValueRange(CodeExpression rhsExpression, Type lhsType, out ValidationError error)
{
error = null;
CodePrimitiveExpression primitive = rhsExpression as CodePrimitiveExpression;
if (primitive != null)
{
try
{
System.Convert.ChangeType(primitive.Value, lhsType, CultureInfo.CurrentCulture);
// If we get here without throwing, it's valid.
return true;
}
catch (Exception e)
{
error = new ValidationError(e.Message, ErrorNumbers.Error_OperandTypesIncompatible);
return false;
}
}
return false;
}
internal bool ValidateMemberAccess(
CodeExpression targetExpression, Type targetType, FieldInfo accessorMethod, string memberName, CodeExpression parentExpr)
{
return this.ValidateMemberAccess(
targetExpression, targetType, memberName, parentExpr,
accessorMethod.DeclaringType.Assembly, RuleValidation.IsPrivate(accessorMethod), RuleValidation.IsInternal(accessorMethod), accessorMethod.IsStatic);
}
internal bool ValidateMemberAccess(
CodeExpression targetExpression, Type targetType, MethodInfo accessorMethod, string memberName, CodeExpression parentExpr)
{
return this.ValidateMemberAccess(
targetExpression, targetType, memberName, parentExpr,
accessorMethod.DeclaringType.Assembly, RuleValidation.IsPrivate(accessorMethod), RuleValidation.IsInternal(accessorMethod), accessorMethod.IsStatic);
}
private bool ValidateMemberAccess(
CodeExpression targetExpression, Type targetType, string memberName, CodeExpression parentExpr,
Assembly methodAssembly, bool isPrivate, bool isInternal, bool isStatic)
{
string message;
if (isStatic != (targetExpression is CodeTypeReferenceExpression))
{
// If it's static, then the target object must be a type ref, and vice versa.
int errorNumber;
if (isStatic)
{
// We have "object.StaticMember"
message = string.Format(CultureInfo.CurrentCulture, Messages.StaticMember, memberName);
errorNumber = ErrorNumbers.Error_StaticMember;
}
else
{
// We have "TypeName.NonStaticMember"
message = string.Format(CultureInfo.CurrentCulture, Messages.NonStaticMember, memberName);
errorNumber = ErrorNumbers.Error_NonStaticMember;
}
ValidationError error = new ValidationError(message, errorNumber);
error.UserData[RuleUserDataKeys.ErrorObject] = parentExpr;
Errors.Add(error);
return false;
}
if (isPrivate && targetType != ThisType)
{
// Can't access private members except on the subject type.
message = string.Format(CultureInfo.CurrentCulture, Messages.CannotAccessPrivateMember, memberName, RuleDecompiler.DecompileType(targetType));
ValidationError error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
error.UserData[RuleUserDataKeys.ErrorObject] = parentExpr;
Errors.Add(error);
return false;
}
if (isInternal && ThisType.Assembly != methodAssembly)
{
// Can't access internal members except on the subject assembly.
message = string.Format(CultureInfo.CurrentCulture, Messages.CannotAccessInternalMember, memberName, RuleDecompiler.DecompileType(targetType));
ValidationError error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
error.UserData[RuleUserDataKeys.ErrorObject] = parentExpr;
Errors.Add(error);
return false;
}
return true;
}
#region Field and property resolution
internal MemberInfo ResolveFieldOrProperty(Type targetType, string name)
{
BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy;
if (AllowInternalMembers(targetType))
bindingFlags |= BindingFlags.NonPublic;
// Look up a field or property of the given name.
MemberInfo[] results = targetType.GetMember(name, MemberTypes.Field | MemberTypes.Property, bindingFlags);
if (results != null)
{
int numResults = results.Length;
if (numResults == 1)
{
// If we found exactly one, we're good.
return results[0];
}
else if (numResults > 1)
{
// We may have found more than one property if it's overloaded. If we find one without
// any parameters, return that one.
for (int i = 0; i < numResults; ++i)
{
MemberInfo member = results[i];
System.Diagnostics.Debug.Assert(member.MemberType == MemberTypes.Property, "only properties can be overloaded");
PropertyInfo pi = (PropertyInfo)member;
ParameterInfo[] parms = pi.GetIndexParameters();
if (parms == null || parms.Length == 0)
{
if (pi != null)
{
IsAuthorized(pi.PropertyType);
}
return pi;
}
}
}
}
// If we didn't find it, and if the target type is an interface, try resolving a property
// that may exist in its inheritance chain. (Fields cannot appear on interfaces.)
if (targetType.IsInterface)
return ResolveProperty(targetType, name, bindingFlags);
// Otherwise, it's no good.
return null;
}
internal PropertyInfo ResolveProperty(Type targetType, string propertyName, BindingFlags bindingFlags)
{
PropertyInfo pi = GetProperty(targetType, propertyName, bindingFlags);
if (pi == null && targetType.IsInterface)
{
Type[] parentInterfacesArray = targetType.GetInterfaces();
List<Type> parentInterfaces = new List<Type>();
parentInterfaces.AddRange(parentInterfacesArray);
int index = 0;
while (index < parentInterfaces.Count)
{
pi = GetProperty(parentInterfaces[index], propertyName, bindingFlags);
if (pi != null)
break;
Type[] pInterfaces = parentInterfaces[index].GetInterfaces();
if (pInterfaces.Length > 0)
parentInterfaces.AddRange(pInterfaces);
++index;
}
}
if (pi != null)
{
IsAuthorized(pi.PropertyType);
}
return pi;
}
private static PropertyInfo GetProperty(Type targetType, string propertyName, BindingFlags bindingFlags)
{
// Properties may be overloaded (in VB), so we have to ---- out those that we can support,
// i.e., those that have no parameters.
MemberInfo[] members = targetType.GetMember(propertyName, MemberTypes.Property, bindingFlags);
for (int m = 0; m < members.Length; ++m)
{
PropertyInfo pi = (PropertyInfo)members[m];
ParameterInfo[] parms = pi.GetIndexParameters();
if (parms == null || parms.Length == 0)
return pi;
}
return null;
}
#endregion
#region Method resolution
private class Argument
{
internal CodeExpression expression;
internal FieldDirection direction;
internal Type type;
internal Argument(CodeExpression expr, RuleValidation validation)
{
this.expression = expr;
this.direction = FieldDirection.In;
CodeDirectionExpression directionExpr = expr as CodeDirectionExpression;
if (directionExpr != null)
this.direction = directionExpr.Direction;
this.type = validation.ExpressionInfo(expr).ExpressionType;
}
internal Argument(Type type)
{
this.direction = FieldDirection.In;
this.type = type;
}
}
private class CandidateParameter
{
private Type type;
private FieldDirection direction;
internal CandidateParameter(Type type)
{
this.type = type;
this.direction = FieldDirection.In;
}
internal CandidateParameter(ParameterInfo paramInfo)
{
this.direction = FieldDirection.In;
if (paramInfo.IsOut)
this.direction = FieldDirection.Out;
else if (paramInfo.ParameterType.IsByRef)
this.direction = FieldDirection.Ref;
this.type = paramInfo.ParameterType;
}
internal bool Match(Argument argument, string methodName, int argPosition, out ValidationError error)
{
string message;
// If we don't agree on the argument direction, this method is not a candidate.
if (this.direction != argument.direction)
{
string dirString = "";
switch (this.direction)
{
case FieldDirection.In:
dirString = "in"; // No localization required, this is a keyword.
break;
case FieldDirection.Out:
dirString = "out"; // No localization required, this is a keyword.
break;
case FieldDirection.Ref:
dirString = "ref"; // No localization required, this is a keyword.
break;
}
message = string.Format(CultureInfo.CurrentCulture, Messages.MethodDirectionMismatch, argPosition, methodName, dirString);
error = new ValidationError(message, ErrorNumbers.Error_MethodDirectionMismatch);
return false;
}
if (this.type.IsByRef && this.type != argument.type)
{
// If the parameter is "ref" or "out", then the types must match exactly.
// If not, this method can't be a candidate.
message = string.Format(CultureInfo.CurrentCulture, Messages.MethodArgumentTypeMismatch, argPosition, methodName, RuleDecompiler.DecompileType(argument.type), RuleDecompiler.DecompileType(this.type));
error = new ValidationError(message, ErrorNumbers.Error_MethodArgumentTypeMismatch);
return false;
}
// If the argument type is not assignable to the corresponding parameter type,
// this method can't be a candidate.
if (!RuleValidation.TypesAreAssignable(argument.type, this.type, argument.expression, out error))
{
if (error == null)
{
message = string.Format(CultureInfo.CurrentCulture, Messages.MethodArgumentTypeMismatch, argPosition, methodName, RuleDecompiler.DecompileType(argument.type), RuleDecompiler.DecompileType(this.type));
error = new ValidationError(message, ErrorNumbers.Error_MethodArgumentTypeMismatch);
}
return false;
}
// If we passed the above checks, this argument is a candidate match for the parameter.
return true;
}
public override bool Equals(object obj)
{
CandidateParameter otherParam = obj as CandidateParameter;
if (otherParam == null)
return false;
return this.direction == otherParam.direction && this.type == otherParam.type;
}
public override int GetHashCode()
{
return this.direction.GetHashCode() ^ this.type.GetHashCode();
}
internal int CompareConversion(CandidateParameter otherParam, Argument argument)
{
// Return 0 if they are equal; 1 if this is better; -1 if this is worse.
// This follows the C# specification 7.4.2.3
int better = 1;
int worse = -1;
int equal = 0;
// If the two candidate parameters have the same type, neither one is better.
if (this.type == otherParam.type)
return equal;
// If the argument type is the same as one of the parameter types, that parameter is better.
if (argument.type == this.type)
return better;
if (argument.type == otherParam.type)
return worse;
// If this parameter can be converted to the other parameter, and not vice versa, then
// this is a better conversion. (And in the reverse situation, it's a worse conversion.)
ValidationError dummy;
bool thisConvertsToOther = RuleValidation.TypesAreAssignable(this.type, otherParam.type, null, out dummy);
bool otherConvertsToThis = RuleValidation.TypesAreAssignable(otherParam.type, this.type, null, out dummy);
if (thisConvertsToOther && !otherConvertsToThis)
return better;
if (otherConvertsToThis && !thisConvertsToOther)
return worse;
// See if one is a better sign-preserving conversion than the other.
if (BetterSignedConversion(this.type, otherParam.type))
return better;
if (BetterSignedConversion(otherParam.type, this.type))
return worse;
// Otherwise, neither conversion is better.
return equal;
}
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
private static bool BetterSignedConversion(Type t1, Type t2)
{
TypeCode tc1 = Type.GetTypeCode(t1);
TypeCode tc2 = Type.GetTypeCode(t2);
switch (tc1)
{
case TypeCode.SByte:
switch (tc2)
{
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
// A conversion to sbyte is better than a conversion to an unsigned type.
return true;
}
break;
case TypeCode.Int16:
switch (tc2)
{
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
// A conversion to short is better than a conversion to an unsigned type.
return true;
}
break;
case TypeCode.Int32:
if (tc2 == TypeCode.UInt32 || tc2 == TypeCode.UInt64)
{
// A conversion to int is better than a conversion to an unsigned type.
return true;
}
break;
case TypeCode.Int64:
if (tc2 == TypeCode.UInt64)
{
// A conversion to long is better than a conversion to an unsigned type.
return true;
}
break;
case TypeCode.Object:
// it is possible that the types are nullable
if (ConditionHelper.IsNullableValueType(t1))
{
t1 = t1.GetGenericArguments()[0];
// t2 may already be a value type
if (ConditionHelper.IsNullableValueType(t2))
t2 = t2.GetGenericArguments()[0];
return BetterSignedConversion(t1, t2);
}
return false;
}
return false;
}
}
private class CandidateMember
{
internal enum Form
{
Normal, // no "params" expansion
Expanded // matched only after "params" expansion
}
internal MemberInfo Member;
private ParameterInfo[] memberParameters;
private List<CandidateParameter> signature;
private Form form;
private static ParameterInfo[] noParameters = new ParameterInfo[0];
private static List<CandidateParameter> noSignature = new List<CandidateParameter>();
// Constructor for candidate methods with parameters.
internal CandidateMember(MemberInfo member, ParameterInfo[] parameters, List<CandidateParameter> signature, Form form)
{
this.Member = member;
this.memberParameters = parameters;
this.signature = signature;
this.form = form;
}
// Constructor for a candidate method that has no parameters.
internal CandidateMember(MemberInfo member)
: this(member, noParameters, noSignature, Form.Normal)
{
}
internal bool IsExpanded
{
get { return form == Form.Expanded; }
}
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
internal int CompareMember(Type targetType, CandidateMember other, List<Argument> arguments, RuleValidation validator)
{
int better = 1;
int worse = -1;
int equal = 0;
// Methods in a base class are not candidates if any method in a derived class
// is applicable.
Type thisDeclaringType = this.Member.DeclaringType;
Type otherDeclaringType = other.Member.DeclaringType;
if (thisDeclaringType != otherDeclaringType)
{
if (TypeProvider.IsAssignable(otherDeclaringType, thisDeclaringType))
{
// This declaring type can be converted to the other declaring type,
// which means this one is more derived.
return better;
}
else if (TypeProvider.IsAssignable(thisDeclaringType, otherDeclaringType))
{
// The other declaring type can be converted to this declaring type,
// which means the other one is more derived.
return worse;
}
}
System.Diagnostics.Debug.Assert(arguments.Count == this.signature.Count);
System.Diagnostics.Debug.Assert(arguments.Count == other.signature.Count);
bool hasAtLeastOneBetterConversion = false;
bool hasAtLeastOneWorseConversion = false;
bool signaturesAreIdentical = true;
// pick non-extension methods over extension methods
// if both are extension methods, then pick the one in the namespace closest to "this"
ExtensionMethodInfo thisExtension = this.Member as ExtensionMethodInfo;
ExtensionMethodInfo otherExtension = other.Member as ExtensionMethodInfo;
if ((thisExtension == null) && (otherExtension != null))
return better;
else if ((thisExtension != null) && (otherExtension == null))
return worse;
else if ((thisExtension != null) && (otherExtension != null))
{
// we have 2 extension methods, which one is better
string[] thisNameSpace = thisExtension.DeclaringType.FullName.Split('.');
string[] otherNameSpace = otherExtension.DeclaringType.FullName.Split('.');
string[] bestNameSpace = validator.thisType.FullName.Split('.');
int thisMatch = MatchNameSpace(thisNameSpace, bestNameSpace);
int otherMatch = MatchNameSpace(otherNameSpace, bestNameSpace);
if (thisMatch > otherMatch)
return better;
else if (thisMatch < otherMatch)
return worse;
// compare arguments, including the "this" argument
CandidateParameter thisDeclaringParam = new CandidateParameter(thisExtension.AssumedDeclaringType);
CandidateParameter otherDeclaringParam = new CandidateParameter(otherExtension.AssumedDeclaringType);
if (!thisDeclaringParam.Equals(otherDeclaringParam))
{
signaturesAreIdentical = false;
int conversionResult = thisDeclaringParam.CompareConversion(otherDeclaringParam, new Argument(targetType));
if (conversionResult < 0)
{
// A conversion was found that was worse, so this candidate is not better.
hasAtLeastOneWorseConversion = true;
}
else if (conversionResult > 0)
{
// This candidate had at least one conversion that was better. (But
// we have to keep looking in case there's one that's worse.)
hasAtLeastOneBetterConversion = true;
}
}
// this check compares parameter lists correctly (see below)
for (int p = 0; p < arguments.Count; ++p)
{
CandidateParameter thisParam = this.signature[p];
CandidateParameter otherParam = other.signature[p];
if (!thisParam.Equals(otherParam))
signaturesAreIdentical = false;
int conversionResult = thisParam.CompareConversion(otherParam, arguments[p]);
if (conversionResult < 0)
{
// A conversion was found that was worse, so this candidate is not better.
hasAtLeastOneWorseConversion = true;
}
else if (conversionResult > 0)
{
// This candidate had at least one conversion that was better. (But
// we have to keep looking in case there's one that's worse.)
hasAtLeastOneBetterConversion = true;
}
}
if (hasAtLeastOneBetterConversion && !hasAtLeastOneWorseConversion)
{
// At least one conversion was better than the "other" candidate
// and no other arguments were worse, so this one is better.
return better;
}
else if (!hasAtLeastOneBetterConversion && hasAtLeastOneWorseConversion)
{
// At least one conversion was worse than the "other" candidate
// and no other arguments were better, so this one is worse.
return worse;
}
}
else
{
// NOTE: this is the original v1 code
// It doesn't check for worse parameters correctly.
// However, for backwards compatability, we can't change it
for (int p = 0; p < arguments.Count; ++p)
{
CandidateParameter thisParam = this.signature[p];
CandidateParameter otherParam = other.signature[p];
if (!thisParam.Equals(otherParam))
signaturesAreIdentical = false;
int conversionResult = thisParam.CompareConversion(otherParam, arguments[p]);
if (conversionResult < 0)
{
// A conversion was found that was worse, so this candidate is not better.
return worse;
}
else if (conversionResult > 0)
{
// This candidate had at least one conversion that was better. (But
// we have to keep looking in case there's one that's worse.)
hasAtLeastOneBetterConversion = true;
}
}
if (hasAtLeastOneBetterConversion)
{
// At least one conversion was better than the "other" candidate, so this one
// is better.
return better;
}
}
if (signaturesAreIdentical)
{
// The signatures were "tied". Try some disambiguating rules for expanded signatures
// vs normal signatures.
if (this.form == Form.Normal && other.form == Form.Expanded)
{
// This candidate matched in its normal form, but the other one matched only after
// expansion of a params array. This one is better.
return better;
}
else if (this.form == Form.Expanded && other.form == Form.Normal)
{
// This candidate matched in its expanded form, but the other one matched in its
// normal form. The other one was better.
return worse;
}
else if (this.form == Form.Expanded && other.form == Form.Expanded)
{
// Both candidates matched in their expanded forms.
int thisParameterCount = this.memberParameters.Length;
int otherParameterCount = other.memberParameters.Length;
if (thisParameterCount > otherParameterCount)
{
// This candidate had more declared parameters, so it is better.
return better;
}
else if (otherParameterCount > thisParameterCount)
{
// The other candidate had more declared parameters, so it was better.
return worse;
}
}
}
// Nothing worked, the two candidates are equally applicable.
return equal;
}
private static int MatchNameSpace(string[] test, string[] reference)
{
// returns the number of strings in test that are the same as reference
int i;
int len = Math.Min(test.Length, reference.Length);
for (i = 0; i < len; ++i)
{
if (test[i] != reference[i])
break;
}
return i;
}
}
// Get the candidate target types, ordered from most-derived to least-derived.
private static List<Type> GetCandidateTargetTypes(Type targetType)
{
List<Type> candidateTypes;
if (targetType.IsInterface)
{
candidateTypes = new List<Type>();
candidateTypes.Add(targetType);
// Add all base interfaces in its hierarchy to the candidate list.
for (int i = 0; i < candidateTypes.Count; ++i)
{
Type currentCandidate = candidateTypes[i];
candidateTypes.AddRange(currentCandidate.GetInterfaces());
}
// Finally, add "System.Object", since all types intrinsically derive from this.
candidateTypes.Add(typeof(object));
}
else
{
// It was a class; just add the one class.
candidateTypes = new List<Type>(1);
candidateTypes.Add(targetType);
}
return candidateTypes;
}
private delegate ValidationError BuildArgCountMismatchError(string name, int numArguments);
private static void EvaluateCandidate(List<CandidateMember> candidates, MemberInfo candidateMember, ParameterInfo[] parameters, List<Argument> arguments, out ValidationError error, BuildArgCountMismatchError buildArgCountMismatchError)
{
error = null;
int numArguments = arguments.Count;
string candidateName = candidateMember.Name;
if (parameters == null || parameters.Length == 0)
{
// If there were no arguments supplied, and this method has no parameters,
// then it's a candidate. (It should be the only one.)
if (numArguments == 0)
{
candidates.Add(new CandidateMember(candidateMember));
}
else
{
error = buildArgCountMismatchError(candidateName, numArguments);
}
}
else
{
List<CandidateParameter> signature = new List<CandidateParameter>();
int parameterCount = parameters.Length;
int fixedParameterCount = parameterCount;
// Check to see if the last parameter is (1) an array and (2) has a ParamArrayAttribute
// (i.e., it is a "params" array).
ParameterInfo lastParam = parameters[parameterCount - 1];
if (lastParam.ParameterType.IsArray)
{
object[] attrs = lastParam.GetCustomAttributes(typeof(ParamArrayAttribute), false);
if (attrs != null && attrs.Length > 0)
fixedParameterCount -= 1;
}
if (numArguments < fixedParameterCount)
{
// Not enough arguments were passed for this to be a candidate.
error = buildArgCountMismatchError(candidateName, numArguments);
return;
}
else if (fixedParameterCount == parameterCount && numArguments != parameterCount)
{
// Too many arguments were passed for this to be a candidate.
error = buildArgCountMismatchError(candidateName, numArguments);
return;
}
// For the fixed part of the method signature, make sure each argument can
// be implicitly converted to the corresponding parameter.
int p = 0;
for (; p < fixedParameterCount; ++p)
{
CandidateParameter candidateParam = new CandidateParameter(parameters[p]);
if (!candidateParam.Match(arguments[p], candidateName, p + 1, out error))
break; // argument #p didn't match
// If we get here, then so far so good.
signature.Add(candidateParam);
}
if (p != fixedParameterCount)
{
// We didn't match all of the fixed part. This method is not a candidate.
return;
}
if (fixedParameterCount < parameterCount)
{
// The last parameter was a "params" array. As long as zero or more arguments
// are assignable, it's a valid candidate in the expanded form.
CandidateMember candidateMethod = null;
if (numArguments == fixedParameterCount)
{
// Zero arguments were passed as the params array. The method is a candidate
// in its expanded form.
candidateMethod = new CandidateMember(candidateMember, parameters, signature, CandidateMember.Form.Expanded);
}
else if (numArguments == parameterCount)
{
// Special case: one argument was passed as the params array.
CandidateParameter candidateParam = new CandidateParameter(lastParam);
if (candidateParam.Match(arguments[p], candidateName, p + 1, out error))
{
// It was the same array type as the params array, so the candidate
// matched in its normal form.
signature.Add(candidateParam);
candidateMethod = new CandidateMember(candidateMember, parameters, signature, CandidateMember.Form.Normal);
}
}
if (candidateMethod == null)
{
// One or more arguments were passed as the params array. As long
// as they match the element type, this method is a candidate.
CandidateParameter candidateParam = new CandidateParameter(lastParam.ParameterType.GetElementType());
for (; p < numArguments; ++p)
{
if (!candidateParam.Match(arguments[p], candidateName, p + 1, out error))
{
// Not all of the trailing arguments matched the params array's element type;
// this cannot be a candidate.
return;
}
// If we get here, then so far so good.
signature.Add(candidateParam);
}
// All the trailing arguments matched, so this is a candidate in the expanded form.
candidateMethod = new CandidateMember(candidateMember, parameters, signature, CandidateMember.Form.Expanded);
}
candidates.Add(candidateMethod);
}
else
{
// The last parameter wasn't "params". This candidate matched in its normal form.
candidates.Add(new CandidateMember(candidateMember, parameters, signature, CandidateMember.Form.Normal));
}
}
}
private CandidateMember FindBestCandidate(Type targetType, List<CandidateMember> candidates, List<Argument> arguments)
{
int numCandidates = candidates.Count;
Debug.Assert(numCandidates > 0, "expected at least one candidate");
// Start by assuming the first candidate is the best one.
List<CandidateMember> bestCandidates = new List<CandidateMember>(1);
bestCandidates.Add(candidates[0]);
// Go through the rest of the candidates and try to find a better one. (If
// there are no more candidates, then there was only one, and that's the right
// one.)
for (int i = 1; i < numCandidates; ++i)
{
CandidateMember newCandidate = candidates[i];
// Compare this new candidate one if the current "best" ones. (If there
// is currently more than one best candidate, then so far its ambiguous, which
// means all the best ones are equally good. Thus if this new candidate
// is better than one, it's better than all.
CandidateMember bestCandidate = bestCandidates[0];
int comparison = newCandidate.CompareMember(targetType, bestCandidate, arguments, this);
if (comparison > 0)
{
// The new one was better than at least one of the best ones. It
// becomes the new best one.
bestCandidates.Clear();
bestCandidates.Add(newCandidate);
}
else if (comparison == 0)
{
// The new one was no better, so add it to the list of current best.
// (Unless we find a better one, it's ambiguous so far.)
bestCandidates.Add(newCandidate);
}
}
if (bestCandidates.Count == 1)
{
// Good, there was exactly one best match.
return bestCandidates[0];
}
// Otherwise, it must have been ambiguous.
return null;
}
internal MethodInfo FindBestCandidate(Type targetType, List<MethodInfo> methods, params Type[] types)
{
List<Argument> arguments = new List<Argument>();
foreach (Type t in types)
arguments.Add(new Argument(t));
List<CandidateMember> candidates = new List<CandidateMember>(methods.Count);
foreach (MethodInfo method in methods)
{
ValidationError tempError = null;
EvaluateCandidate(candidates, method, method.GetParameters(), arguments, out tempError,
delegate(string name, int numArguments)
{
string message = string.Format(CultureInfo.CurrentCulture, Messages.MethodArgCountMismatch, name, numArguments);
return new ValidationError(message, ErrorNumbers.Error_MethodArgCountMismatch);
});
}
if (candidates.Count == 0)
{
// nothing looks useful
return null;
}
CandidateMember result = FindBestCandidate(targetType, candidates, arguments);
return (result != null) ? (MethodInfo)result.Member : null;
}
internal RuleConstructorExpressionInfo ResolveConstructor(Type targetType, BindingFlags constructorBindingFlags, List<CodeExpression> argumentExprs, out ValidationError error)
{
string message;
List<Argument> arguments = new List<Argument>(argumentExprs.Count);
foreach (CodeExpression argumentExpr in argumentExprs)
arguments.Add(new Argument(argumentExpr, this));
// Get the candidate types and all candidate methods contained in them.
List<Type> candidateTypes = GetCandidateTargetTypes(targetType);
// Get all methods by this name...
List<ConstructorInfo> constructors = GetConstructors(candidateTypes, constructorBindingFlags);
if (constructors.Count == 0)
{
message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownConstructor, RuleDecompiler.DecompileType(targetType));
error = new ValidationError(message, ErrorNumbers.Error_MethodNotExists);
return null;
}
// Cull the list of methods to those which match the supplied arguments.
List<CandidateMember> candidateConstructors = GetCandidateConstructors(constructors, arguments, out error);
// If the list is null, then no candidates matched.
if (candidateConstructors == null)
return null;
// We found candidate methods in this type.
CandidateMember bestCandidate = FindBestCandidate(targetType, candidateConstructors, arguments);
if (bestCandidate == null)
{
// It was ambiguous.
message = string.Format(CultureInfo.CurrentCulture, Messages.AmbiguousConstructor, RuleDecompiler.DecompileType(targetType));
error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
return null;
}
// We found the best match.
return new RuleConstructorExpressionInfo((ConstructorInfo)bestCandidate.Member, bestCandidate.IsExpanded);
}
internal RuleMethodInvokeExpressionInfo ResolveMethod(Type targetType, string methodName, BindingFlags methodBindingFlags, List<CodeExpression> argumentExprs, out ValidationError error)
{
string message;
List<Argument> arguments = new List<Argument>(argumentExprs.Count);
foreach (CodeExpression argumentExpr in argumentExprs)
arguments.Add(new Argument(argumentExpr, this));
// Get the candidate types and all candidate methods contained in them.
List<Type> candidateTypes = GetCandidateTargetTypes(targetType);
// Get all methods by this name...
List<MethodInfo> methods = GetNamedMethods(candidateTypes, methodName, methodBindingFlags);
if (methods.Count == 0)
{
message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownMethod, methodName, RuleDecompiler.DecompileType(targetType));
error = new ValidationError(message, ErrorNumbers.Error_MethodNotExists);
return null;
}
// Cull the list of methods to those which match the supplied arguments.
List<CandidateMember> candidateMethods = GetCandidateMethods(methodName, methods, arguments, out error);
// If the list is null, then no candidates matched.
if (candidateMethods == null)
return null;
// We found candidate methods in this type.
CandidateMember bestCandidate = FindBestCandidate(targetType, candidateMethods, arguments);
if (bestCandidate == null)
{
// It was ambiguous.
message = string.Format(CultureInfo.CurrentCulture, Messages.AmbiguousMatch, methodName);
error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
return null;
}
// We found the best match.
MethodInfo theMethod = (MethodInfo)bestCandidate.Member;
if (theMethod != null)
{
IsAuthorized(theMethod.ReturnType);
}
return new RuleMethodInvokeExpressionInfo(theMethod, bestCandidate.IsExpanded);
}
internal static List<ConstructorInfo> GetConstructors(List<Type> targetTypes, BindingFlags constructorBindingFlags)
{
List<ConstructorInfo> methods = new List<ConstructorInfo>();
for (int t = 0; t < targetTypes.Count; ++t)
{
Type targetType = targetTypes[t];
// Go through all the constructors on the target type
ConstructorInfo[] members = targetType.GetConstructors(constructorBindingFlags);
for (int m = 0; m < members.Length; ++m)
{
ConstructorInfo constructor = members[m];
if (constructor.IsGenericMethod) // skip generic constructors
continue;
if (constructor.IsStatic) // skip static constructors
continue;
if (constructor.IsPrivate) // skip private constructors
continue;
if (constructor.IsFamily) // skip internal constructors
continue;
methods.Add(constructor);
}
}
return methods;
}
private List<MethodInfo> GetNamedMethods(List<Type> targetTypes, string methodName, BindingFlags methodBindingFlags)
{
List<MethodInfo> methods = new List<MethodInfo>();
List<ExtensionMethodInfo> currentExtensionMethods = ExtensionMethods;
for (int t = 0; t < targetTypes.Count; ++t)
{
Type targetType = targetTypes[t];
// Go through all the methods on the target type that have matching names.
MemberInfo[] members = targetType.GetMember(methodName, MemberTypes.Method, methodBindingFlags);
for (int m = 0; m < members.Length; ++m)
{
MethodInfo method = (MethodInfo)members[m];
if (!method.IsGenericMethod) // skip generic methods
methods.Add(method);
}
// add in any extension methods that match
foreach (ExtensionMethodInfo extension in currentExtensionMethods)
{
// does it have the right name and is the type compatible
ValidationError error;
if ((extension.Name == methodName) &&
TypesAreAssignable(targetType, extension.AssumedDeclaringType, null, out error))
{
// possible match
methods.Add(extension);
}
}
}
return methods;
}
private List<ExtensionMethodInfo> extensionMethods;
private List<Assembly> seenAssemblies;
private const string ExtensionAttributeFullName = "System.Runtime.CompilerServices.ExtensionAttribute, " + AssemblyRef.SystemCore;
private Type extensionAttribute;
private static Type defaultExtensionAttribute = GetDefaultExtensionAttribute();
private static Type GetDefaultExtensionAttribute()
{
return Type.GetType(ExtensionAttributeFullName, false);
}
// The extensionAttributeType may still be null after calling this method
// if, for example, we are in a 3.0 SP2 environment.
private void SetExtensionAttribute()
{
// use the TypeProvider first
extensionAttribute = typeProvider.GetType(ExtensionAttributeFullName, false);
if (extensionAttribute == null)
{
extensionAttribute = defaultExtensionAttribute;
}
}
internal List<ExtensionMethodInfo> ExtensionMethods
{
get
{
if (extensionMethods == null)
DetermineExtensionMethods();
return extensionMethods;
}
}
private void DetermineExtensionMethods()
{
extensionMethods = new List<ExtensionMethodInfo>();
SetExtensionAttribute();
if (extensionAttribute != null)
{
seenAssemblies = new List<Assembly>();
Assembly localAssembly = typeProvider.LocalAssembly;
if (localAssembly != null)
{
DetermineExtensionMethods(localAssembly);
foreach (Assembly a in typeProvider.ReferencedAssemblies)
DetermineExtensionMethods(a);
}
else
{
// probably at design-time, nothing compiled yet
// go through all types it knows about
DetermineExtensionMethods(typeProvider.GetTypes());
}
}
}
internal void DetermineExtensionMethods(Assembly assembly)
{
// when this method is called outside of this class, we must have tried
// getting ExtensionMethods. So we must have tried setting extensionAttributeType.
if (extensionAttribute != null)
{
if ((assembly != null) && (!seenAssemblies.Contains(assembly)))
{
seenAssemblies.Add(assembly);
if (IsMarkedExtension(assembly))
{
Type[] types;
try
{
types = assembly.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
// problems loading all the types, take what we can get
// some types will be null
types = e.Types;
}
DetermineExtensionMethods(types);
}
}
}
}
private void DetermineExtensionMethods(Type[] types)
{
foreach (Type type in types)
{
// static classes are defined as "abstract sealed"
// Note: VB doesn't support static classes, so the modules are only defined as "sealed"
if ((type != null) && (type.IsPublic || type.IsNestedPublic) && (type.IsSealed) && (IsMarkedExtension(type)))
{
// looks like a class containing extension methods, let's find them
MethodInfo[] staticMethods = type.GetMethods(BindingFlags.Static | BindingFlags.Public);
foreach (MethodInfo mi in staticMethods)
{
// skip generic methods
if ((mi.IsStatic) && !(mi.IsGenericMethod) && (IsMarkedExtension(mi)))
{
ParameterInfo[] parms = mi.GetParameters();
if (parms.Length > 0 && parms[0].ParameterType != null)
{
extensionMethods.Add(new ExtensionMethodInfo(mi, parms));
}
}
}
}
}
}
private bool IsMarkedExtension(Assembly assembly)
{
if (extensionAttribute != null)
{
object[] objAttrs = assembly.GetCustomAttributes(extensionAttribute, false);
if (objAttrs != null && objAttrs.Length > 0)
return true;
}
return false;
}
private bool IsMarkedExtension(Type type)
{
if (extensionAttribute != null)
{
object[] objAttrs = type.GetCustomAttributes(extensionAttribute, false);
if (objAttrs != null && objAttrs.Length > 0)
return true;
}
return false;
}
private bool IsMarkedExtension(MethodInfo mi)
{
if (extensionAttribute != null)
{
object[] objAttrs = mi.GetCustomAttributes(extensionAttribute, false);
if (objAttrs != null && objAttrs.Length > 0)
return true;
}
return false;
}
static List<CandidateMember> GetCandidateMethods(string methodName, List<MethodInfo> methods, List<Argument> arguments, out ValidationError error)
{
List<CandidateMember> candidates = new List<CandidateMember>();
error = null;
int errorCount = 0;
foreach (MethodInfo method in methods)
{
ValidationError tempError = null;
EvaluateCandidate(candidates, method, method.GetParameters(), arguments, out tempError,
delegate(string name, int numArguments)
{
string message = string.Format(CultureInfo.CurrentCulture, Messages.MethodArgCountMismatch, name, numArguments);
return new ValidationError(message, ErrorNumbers.Error_MethodArgCountMismatch);
});
error = tempError;
if (tempError != null)
++errorCount;
}
if (candidates.Count == 0)
{
// No candidates were found.
if (errorCount > 1)
{
// If multiple candidates generated errors, then use a more generic error that says
// we couldn't find a matching overload.
string message = string.Format(CultureInfo.CurrentCulture, Messages.MethodOverloadNotFound, methodName);
error = new ValidationError(message, ErrorNumbers.Error_MethodOverloadNotFound);
}
return null;
}
else
{
// If there are any candidates, then wipe out any errors left over from any mismatches.
error = null;
}
return candidates;
}
static List<CandidateMember> GetCandidateConstructors(List<ConstructorInfo> constructors, List<Argument> arguments, out ValidationError error)
{
List<CandidateMember> candidates = new List<CandidateMember>();
error = null;
int errorCount = 0;
foreach (ConstructorInfo method in constructors)
{
ValidationError tempError = null;
EvaluateCandidate(candidates, method, method.GetParameters(), arguments, out tempError,
delegate(string name, int numArguments)
{
string message = string.Format(CultureInfo.CurrentCulture, Messages.MethodArgCountMismatch, name, numArguments);
return new ValidationError(message, ErrorNumbers.Error_MethodArgCountMismatch);
});
error = tempError;
if (tempError != null)
++errorCount;
}
if (candidates.Count == 0)
{
// No candidates were found.
if (errorCount > 1)
{
// If multiple candidates generated errors, then use a more generic error that says
// we couldn't find a matching overload.
string message = string.Format(CultureInfo.CurrentCulture, Messages.ConstructorOverloadNotFound);
error = new ValidationError(message, ErrorNumbers.Error_MethodOverloadNotFound);
}
return null;
}
else
{
// If there are any candidates, then wipe out any errors left over from any mismatches.
error = null;
}
return candidates;
}
internal RulePropertyExpressionInfo ResolveIndexerProperty(Type targetType, BindingFlags bindingFlags, List<CodeExpression> argumentExprs, out ValidationError error)
{
string message;
int numArgs = argumentExprs.Count;
if (numArgs < 1)
{
// Must have at least one indexer!
message = string.Format(CultureInfo.CurrentCulture, Messages.IndexerCountMismatch, numArgs);
error = new ValidationError(message, ErrorNumbers.Error_IndexerCountMismatch);
return null;
}
List<Argument> arguments = new List<Argument>(numArgs);
foreach (CodeExpression argumentExpr in argumentExprs)
arguments.Add(new Argument(argumentExpr, this));
// Get the candidate types and all the candidate indexer properties contained in them.
List<Type> candidateTypes = GetCandidateTargetTypes(targetType);
List<PropertyInfo> indexerProperties = GetIndexerProperties(candidateTypes, bindingFlags);
if (indexerProperties.Count == 0)
{
message = string.Format(CultureInfo.CurrentCulture, Messages.IndexerNotFound, RuleDecompiler.DecompileType(targetType));
error = new ValidationError(message, ErrorNumbers.Error_IndexerNotFound);
return null;
}
List<CandidateMember> candidateIndexers = GetCandidateIndexers(indexerProperties, arguments, out error);
// If the list is null, then no candidates matched.
if (candidateIndexers == null)
return null;
// We found candidate methods in this type.
CandidateMember bestCandidate = FindBestCandidate(targetType, candidateIndexers, arguments);
if (bestCandidate == null)
{
// It was ambiguous.
message = string.Format(CultureInfo.CurrentCulture, Messages.AmbiguousIndexerMatch);
error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
return null;
}
// We found the best match.
PropertyInfo pi = (PropertyInfo)bestCandidate.Member;
if (pi != null)
{
IsAuthorized(pi.PropertyType);
}
return new RulePropertyExpressionInfo(pi, pi.PropertyType, bestCandidate.IsExpanded);
}
private static List<PropertyInfo> GetIndexerProperties(List<Type> candidateTypes, BindingFlags bindingFlags)
{
List<PropertyInfo> indexerProperties = new List<PropertyInfo>();
foreach (Type targetType in candidateTypes)
{
object[] attrs = targetType.GetCustomAttributes(typeof(DefaultMemberAttribute), true);
if (attrs == null || attrs.Length == 0)
continue;
DefaultMemberAttribute[] defaultMemberAttrs = (DefaultMemberAttribute[])attrs;
PropertyInfo[] properties = targetType.GetProperties(bindingFlags);
for (int p = 0; p < properties.Length; ++p)
{
PropertyInfo pi = properties[p];
// Select only those properties whose name matches the default name.
bool matchedName = false;
for (int dm = 0; dm < defaultMemberAttrs.Length; ++dm)
{
if (defaultMemberAttrs[dm].MemberName == pi.Name)
{
matchedName = true;
break;
}
}
if (matchedName)
{
// We matched the name...
ParameterInfo[] indexerParameters = pi.GetIndexParameters();
if (indexerParameters.Length > 0)
{
// ... and have indexer parameters; therefore, this is
// an interesting property.
indexerProperties.Add(pi);
}
}
}
}
return indexerProperties;
}
private static List<CandidateMember> GetCandidateIndexers(List<PropertyInfo> indexerProperties, List<Argument> arguments, out ValidationError error)
{
List<CandidateMember> candidates = new List<CandidateMember>();
error = null;
int errorCount = 0;
foreach (PropertyInfo indexerProp in indexerProperties)
{
ValidationError tempError = null;
EvaluateCandidate(candidates, indexerProp, indexerProp.GetIndexParameters(), arguments, out tempError,
delegate(string propName, int numArguments)
{
string message = string.Format(CultureInfo.CurrentCulture, Messages.IndexerCountMismatch, numArguments);
return new ValidationError(message, ErrorNumbers.Error_IndexerCountMismatch);
});
error = tempError;
if (tempError != null)
++errorCount;
}
if (candidates.Count == 0)
{
// No candidates were found.
if (errorCount > 1)
{
// If multiple candidates generated errors, then use a more generic error that says
// we couldn't find a matching overload.
string message = string.Format(CultureInfo.CurrentCulture, Messages.IndexerOverloadNotFound);
error = new ValidationError(message, ErrorNumbers.Error_IndexerOverloadNotFound);
}
return null;
}
else
{
// If there are any candidates, then wipe out any errors left over from any mismatches.
error = null;
}
return candidates;
}
#endregion
#region Type resolution
internal void AddTypeReference(CodeTypeReference typeRef, Type type)
{
typeRefMap[typeRef] = type;
}
internal Type ResolveType(CodeTypeReference typeRef)
{
Type resultType = null;
if (!typeRefMap.TryGetValue(typeRef, out resultType))
{
string message;
resultType = FindType(typeRef.BaseType);
if (resultType == null)
{
// check if we have a qualifiedname saved, and if we do, use it
string qualifiedName = typeRef.UserData[RuleUserDataKeys.QualifiedName] as string;
resultType = ResolveType(qualifiedName);
if (resultType != null)
{
// qualified name returned the complete type, save it and we're done
typeRefMap.Add(typeRef, resultType);
return resultType;
}
message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownType, typeRef.BaseType);
ValidationError error = new ValidationError(message, ErrorNumbers.Error_UnableToResolveType);
error.UserData[RuleUserDataKeys.ErrorObject] = typeRef;
Errors.Add(error);
return null;
}
// Handle generic type arguments.
if (typeRef.TypeArguments.Count > 0)
{
Type[] typeArguments = new Type[typeRef.TypeArguments.Count];
for (int i = 0; i < typeRef.TypeArguments.Count; ++i)
{
// design-time types don't have fully-qualified names, so when they are
// used in a generic CodeTypeReference constructor leaves them with []
// surrounding them. Remove the [] if possible
CodeTypeReference arg = typeRef.TypeArguments[i];
if (arg.BaseType.StartsWith("[", StringComparison.Ordinal))
arg.BaseType = arg.BaseType.Substring(1, arg.BaseType.Length - 2);
typeArguments[i] = ResolveType(arg);
if (typeArguments[i] == null)
return null;
}
resultType = resultType.MakeGenericType(typeArguments);
if (resultType == null)
{
StringBuilder sb = new StringBuilder(typeRef.BaseType);
string prefix = "<";
foreach (Type t in typeArguments)
{
sb.Append(prefix);
prefix = ",";
sb.Append(RuleDecompiler.DecompileType(t));
}
sb.Append(">");
message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownGenericType, sb.ToString());
ValidationError error = new ValidationError(message, ErrorNumbers.Error_UnableToResolveType);
error.UserData[RuleUserDataKeys.ErrorObject] = typeRef;
Errors.Add(error);
return null;
}
}
if (resultType != null)
{
CodeTypeReference arrayTypeRef = typeRef;
if (arrayTypeRef.ArrayRank > 0)
{
do
{
resultType = (arrayTypeRef.ArrayRank == 1) ? resultType.MakeArrayType() : resultType.MakeArrayType(arrayTypeRef.ArrayRank);
arrayTypeRef = arrayTypeRef.ArrayElementType;
} while (arrayTypeRef.ArrayRank > 0);
}
}
if (resultType != null)
{
typeRefMap.Add(typeRef, resultType);
// at runtime we may not have the assembly loaded, so keep the fully qualified name around
typeRef.UserData[RuleUserDataKeys.QualifiedName] = resultType.AssemblyQualifiedName;
}
}
return resultType;
}
internal Type ResolveType(string qualifiedName)
{
Type resultType = null;
if (qualifiedName != null)
{
resultType = typeProvider.GetType(qualifiedName, false);
// if the Typeprovider can't find it, use the framework,
// since it should be an AssemblyQualifiedName
if (resultType == null)
resultType = Type.GetType(qualifiedName, false);
}
return resultType;
}
private Type FindType(string typeName)
{
if (typeName == null)
throw new ArgumentNullException("typeName");
Type type = null;
// do we know about this type
if (!typesUsed.TryGetValue(typeName, out type))
{
type = typeProvider.GetType(typeName, false);
if (type != null)
{
typesUsed.Add(typeName, type);
IsAuthorized(type);
}
}
return type;
}
internal void IsAuthorized(Type type)
{
Debug.Assert(!type.IsPointer && !type.IsByRef,
"IsAuthorized should not be called for a type that is a pointer or passed by reference : " + type.AssemblyQualifiedName);
if (checkStaticType)
{
if (authorizedTypes == null)
{
ValidationError error = new ValidationError(Messages.Error_ConfigFileMissingOrInvalid, ErrorNumbers.Error_ConfigFileMissingOrInvalid);
Errors.Add(error);
}
else
{
while (type.IsArray)
{
type = type.GetElementType();
}
if (type.IsGenericType)
{
IsAuthorizedSimpleType(type.GetGenericTypeDefinition());
Type[] typeArguments = type.GetGenericArguments();
foreach (Type t in typeArguments)
{
IsAuthorized(t);
}
}
else
{
IsAuthorizedSimpleType(type);
}
}
}
}
void IsAuthorizedSimpleType(Type type)
{
Debug.Assert((!type.IsGenericType || type.IsGenericTypeDefinition) && !type.HasElementType,
"IsAuthorizedSimpleType should not be called for a partially specialized generic type or a type that encompasses or refers to another type : " +
type.AssemblyQualifiedName);
string qualifiedName = type.AssemblyQualifiedName;
if (!typesUsedAuthorized.ContainsKey(qualifiedName))
{
bool authorized = false;
foreach (AuthorizedType authorizedType in authorizedTypes)
{
if (authorizedType.RegularExpression.IsMatch(qualifiedName))
{
authorized = (String.Compare(bool.TrueString, authorizedType.Authorized, StringComparison.OrdinalIgnoreCase) == 0);
if (!authorized)
break;
}
}
if (!authorized)
{
string message = string.Format(CultureInfo.CurrentCulture, Messages.Error_TypeNotAuthorized, type.FullName);
ValidationError error = new ValidationError(message, ErrorNumbers.Error_TypeNotAuthorized);
error.UserData[RuleUserDataKeys.ErrorObject] = type;
Errors.Add(error);
}
else
{
typesUsedAuthorized.Add(qualifiedName, type);
}
}
}
#endregion
#endregion
}
#endregion RuleValidator
}
|