File: Rules\Expressions.cs
Project: ndp\cdf\src\WF\Activities\System.Workflow.Activities.csproj (System.Workflow.Activities)
// ---------------------------------------------------------------------------
// Copyright (C) 2006 Microsoft Corporation All Rights Reserved
// ---------------------------------------------------------------------------
 
#define CODE_ANALYSIS
using System.CodeDom;
using System.Collections.Generic;
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
{
    public interface IRuleExpression
    {
        RuleExpressionInfo Validate(RuleValidation validation, bool isWritten);
        RuleExpressionResult Evaluate(RuleExecution execution);
        void AnalyzeUsage(RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier);
        [SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters", MessageId = "0#")]
        void Decompile(StringBuilder stringBuilder, CodeExpression parentExpression);
        bool Match(CodeExpression expression);
        CodeExpression Clone();
    }
 
    internal abstract class RuleExpressionInternal
    {
        internal abstract RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten);
        internal abstract RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution);
        internal abstract void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier);
        internal abstract void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression);
        internal abstract bool Match(CodeExpression leftExpression, CodeExpression rightExpression);
        internal abstract CodeExpression Clone(CodeExpression expression);
    }
 
 
    #region "this" expression
 
    // CodeThisReferenceExpression
    internal class ThisExpression : RuleExpressionInternal
    {
        internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
        {
            if (isWritten)
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeThisReferenceExpression).ToString());
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
                error.UserData[RuleUserDataKeys.ErrorObject] = expression;
                validation.Errors.Add(error);
                return null;
            }
 
            return new RuleExpressionInfo(validation.ThisType);
        }
 
        internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
        {
            if (analysis.ForWrites && !isWritten)            // If we're tracking writes, then ignore things that aren't written.
                return;
            else if (!analysis.ForWrites && !isRead)   // ... and vice-versa
                return;
 
            StringBuilder sb = new StringBuilder("this/");
            for (RulePathQualifier q = qualifier; q != null; q = q.Next)
            {
                sb.Append(q.Name);
                if (q.Name == "*")
                {
                    if (q.Next != null)
                        throw new NotSupportedException(Messages.InvalidWildCardInPathQualifier);
                }
                else
                {
                    sb.Append("/");
                }
            }
 
            // Add the symbol to our set.
            analysis.AddSymbol(sb.ToString());
        }
 
        internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
        {
            return execution.ThisLiteralResult;
        }
 
        internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
        {
            stringBuilder.Append("this");
        }
 
        internal override CodeExpression Clone(CodeExpression expression)
        {
            return new CodeThisReferenceExpression();
        }
 
        internal override bool Match(CodeExpression expression, CodeExpression comperand)
        {
            // We already verified their types match.
            return true;
        }
    }
 
    #endregion
 
    #region Primitive expression
 
    // CodePrimitiveExpression
    internal class PrimitiveExpression : RuleExpressionInternal
    {
        internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
        {
            if (isWritten)
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodePrimitiveExpression).ToString());
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
                error.UserData[RuleUserDataKeys.ErrorObject] = expression;
                validation.Errors.Add(error);
                return null;
            }
 
            CodePrimitiveExpression primitiveExpr = (CodePrimitiveExpression)expression;
            Type resultType = (primitiveExpr.Value != null) ? primitiveExpr.Value.GetType() : typeof(NullLiteral);
            return new RuleExpressionInfo(resultType);
        }
 
        internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
        {
            // Literal values have no interesting dependencies or side-effects.
        }
 
        internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
        {
            CodePrimitiveExpression primitiveExpr = (CodePrimitiveExpression)expression;
            return new RuleLiteralResult(primitiveExpr.Value);
        }
 
        internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
        {
            CodePrimitiveExpression primitiveExpr = (CodePrimitiveExpression)expression;
            RuleDecompiler.DecompileObjectLiteral(stringBuilder, primitiveExpr.Value);
        }
 
        internal override CodeExpression Clone(CodeExpression expression)
        {
            CodePrimitiveExpression primitiveExpr = (CodePrimitiveExpression)expression;
            object clonedValue = ConditionHelper.CloneObject(primitiveExpr.Value);
            return new CodePrimitiveExpression(clonedValue);
        }
 
        internal override bool Match(CodeExpression expression, CodeExpression comperand)
        {
            CodePrimitiveExpression primitiveExpr = (CodePrimitiveExpression)expression;
            CodePrimitiveExpression comperandPrimitive = (CodePrimitiveExpression)comperand;
 
            if (primitiveExpr.Value == comperandPrimitive.Value)
                return true;
 
            if (primitiveExpr.Value == null || comperandPrimitive.Value == null)
                return false;
 
            return primitiveExpr.Value.Equals(comperandPrimitive.Value);
        }
    }
 
    #endregion
 
    #region Binary expression
 
    // CodeBinaryOperatorExpression
    internal class BinaryExpression : RuleExpressionInternal
    {
        #region Validate
 
        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
        {
            string message;
            ValidationError error;
 
            CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
 
            // Early exit from this if a cycle is detected.
            if (!validation.PushParentExpression(binaryExpr))
                return null;
 
            if (isWritten)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeBinaryOperatorExpression).ToString());
                error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
                error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
                validation.Errors.Add(error);
            }
 
            RuleExpressionInfo lhsExprInfo = null;
            RuleExpressionInfo rhsExprInfo = null;
 
            if (binaryExpr.Left == null)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.NullBinaryOpLHS, binaryExpr.Operator.ToString());
                error = new ValidationError(message, ErrorNumbers.Error_LeftOperandMissing);
                error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
                validation.Errors.Add(error);
            }
            else
            {
                if (binaryExpr.Left is CodeTypeReferenceExpression)
                {
                    message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, binaryExpr.Left.GetType().FullName);
                    error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
                    error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr.Left;
                    validation.AddError(error);
                    return null;
                }
 
                lhsExprInfo = RuleExpressionWalker.Validate(validation, binaryExpr.Left, false);
            }
 
            if (binaryExpr.Right == null)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.NullBinaryOpRHS, binaryExpr.Operator.ToString());
                error = new ValidationError(message, ErrorNumbers.Error_RightOperandMissing);
                error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
                validation.Errors.Add(error);
            }
            else
            {
                if (binaryExpr.Right is CodeTypeReferenceExpression)
                {
                    message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, binaryExpr.Right.GetType().FullName);
                    error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
                    error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr.Right;
                    validation.AddError(error);
                    return null;
                }
 
                rhsExprInfo = RuleExpressionWalker.Validate(validation, binaryExpr.Right, false);
            }
 
            validation.PopParentExpression();
 
            RuleBinaryExpressionInfo resultExprInfo = null;
 
            if (lhsExprInfo != null && rhsExprInfo != null)
            {
                Type lhsType = lhsExprInfo.ExpressionType;
                Type rhsType = rhsExprInfo.ExpressionType;
 
                switch (binaryExpr.Operator)
                {
                    case CodeBinaryOperatorType.Add:
                    case CodeBinaryOperatorType.Subtract:
                    case CodeBinaryOperatorType.Multiply:
                    case CodeBinaryOperatorType.Divide:
                    case CodeBinaryOperatorType.Modulus:
                    case CodeBinaryOperatorType.BitwiseAnd:
                    case CodeBinaryOperatorType.BitwiseOr:
                        resultExprInfo = ArithmeticLiteral.ResultType(binaryExpr.Operator, lhsType, binaryExpr.Left, rhsType, binaryExpr.Right, validation, out error);
                        if (resultExprInfo == null)
                        {
                            // check if constants are used with ulongs, as we should do some extra "conversions"
                            if (((lhsType == typeof(ulong)) && (PromotionPossible(rhsType, binaryExpr.Right)))
                                || ((rhsType == typeof(ulong)) && (PromotionPossible(lhsType, binaryExpr.Left))))
                            {
                                resultExprInfo = new RuleBinaryExpressionInfo(lhsType, rhsType, typeof(ulong));
                            }
                            else
                            {
                                error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
                                validation.Errors.Add(error);
                            }
                        }
                        break;
 
                    case CodeBinaryOperatorType.IdentityEquality:
                    case CodeBinaryOperatorType.IdentityInequality:
                        resultExprInfo = new RuleBinaryExpressionInfo(lhsType, rhsType, typeof(bool));
                        break;
 
                    case CodeBinaryOperatorType.ValueEquality:
                        resultExprInfo = Literal.AllowedComparison(lhsType, binaryExpr.Left, rhsType, binaryExpr.Right, binaryExpr.Operator, validation, out error);
                        if (resultExprInfo == null)
                        {
                            // check if constants are used with ulongs, as we should do some extra "conversions"
                            if (((lhsType == typeof(ulong)) && (PromotionPossible(rhsType, binaryExpr.Right)))
                                || ((rhsType == typeof(ulong)) && (PromotionPossible(lhsType, binaryExpr.Left))))
                            {
                                resultExprInfo = new RuleBinaryExpressionInfo(lhsType, rhsType, typeof(bool));
                            }
                            else
                            {
                                error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
                                validation.Errors.Add(error);
                            }
                        }
                        break;
 
                    case CodeBinaryOperatorType.LessThan:
                    case CodeBinaryOperatorType.LessThanOrEqual:
                    case CodeBinaryOperatorType.GreaterThan:
                    case CodeBinaryOperatorType.GreaterThanOrEqual:
                        resultExprInfo = Literal.AllowedComparison(lhsType, binaryExpr.Left, rhsType, binaryExpr.Right, binaryExpr.Operator, validation, out error);
                        if (resultExprInfo == null)
                        {
                            // check if constants are used with ulongs, as we should do some extra "conversions"
                            if (((lhsType == typeof(ulong)) && (PromotionPossible(rhsType, binaryExpr.Right)))
                                || ((rhsType == typeof(ulong)) && (PromotionPossible(lhsType, binaryExpr.Left))))
                            {
                                resultExprInfo = new RuleBinaryExpressionInfo(lhsType, rhsType, typeof(bool));
                            }
                            else
                            {
                                error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
                                validation.Errors.Add(error);
                            }
                        }
                        break;
 
                    case CodeBinaryOperatorType.BooleanAnd:
                    case CodeBinaryOperatorType.BooleanOr:
                        resultExprInfo = new RuleBinaryExpressionInfo(lhsType, rhsType, typeof(bool));
                        if (lhsType != typeof(bool))
                        {
                            message = string.Format(CultureInfo.CurrentCulture, Messages.LogicalOpBadTypeLHS, binaryExpr.Operator.ToString(),
                                (lhsType == typeof(NullLiteral)) ? Messages.NullValue : RuleDecompiler.DecompileType(lhsType));
                            error = new ValidationError(message, ErrorNumbers.Error_LeftOperandInvalidType);
                            error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
                            validation.Errors.Add(error);
                            resultExprInfo = null;
                        }
                        if (rhsType != typeof(bool))
                        {
                            message = string.Format(CultureInfo.CurrentCulture, Messages.LogicalOpBadTypeRHS, binaryExpr.Operator.ToString(),
                                (rhsType == typeof(NullLiteral)) ? Messages.NullValue : RuleDecompiler.DecompileType(rhsType));
                            error = new ValidationError(message, ErrorNumbers.Error_RightOperandInvalidType);
                            error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
                            validation.Errors.Add(error);
                            resultExprInfo = null;
                        }
                        break;
 
                    default:
                        {
                            message = string.Format(CultureInfo.CurrentCulture, Messages.BinaryOpNotSupported, binaryExpr.Operator.ToString());
                            error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
                            error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
                            validation.Errors.Add(error);
                        }
                        break;
                }
            }
 
            // Validate any RuleAttributes, if present.
            if (resultExprInfo != null)
            {
                MethodInfo method = resultExprInfo.MethodInfo;
                if (method != null)
                {
                    object[] attrs = method.GetCustomAttributes(typeof(RuleAttribute), true);
                    if (attrs != null && attrs.Length > 0)
                    {
                        Stack<MemberInfo> methodStack = new Stack<MemberInfo>();
                        methodStack.Push(method);
 
                        bool allAttributesValid = true;
                        foreach (RuleAttribute ruleAttr in attrs)
                        {
                            if (!ruleAttr.Validate(validation, method, method.DeclaringType, method.GetParameters()))
                                allAttributesValid = false;
                        }
 
                        methodStack.Pop();
 
                        if (!allAttributesValid)
                            return null;
                    }
                }
            }
 
            return resultExprInfo;
        }
 
        /// <summary>
        /// Check that the expression is a constant, and is promotable to type ULONG.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
        private static bool PromotionPossible(Type type, CodeExpression expression)
        {
            // C# 2.0, section 6.1.6, int/long constants can be promoted to ulong as long as in range
            if (type == typeof(int))
            {
                CodePrimitiveExpression primitive = expression as CodePrimitiveExpression;
                if (primitive != null)
                {
                    int i = (int)primitive.Value;
                    return (i >= 0);
                }
            }
            else if (type == typeof(long))
            {
                CodePrimitiveExpression primitive = expression as CodePrimitiveExpression;
                if (primitive != null)
                {
                    long l = (long)primitive.Value;
                    return (l >= 0);
                }
            }
            return false;
        }
        #endregion
 
        internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
        {
            CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
 
            // Get the method info from the validation so we can look for [RuleRead] and [RuleWrite] attributes.
            RuleBinaryExpressionInfo expressionInfo = analysis.Validation.ExpressionInfo(binaryExpr) as RuleBinaryExpressionInfo;
            if (expressionInfo != null)
            {
                // we may be calling a method, not a default operator
                MethodInfo method = expressionInfo.MethodInfo;
                if (method != null)
                {
                    List<CodeExpression> attributedExprs = new List<CodeExpression>();
                    CodeExpressionCollection arguments = new CodeExpressionCollection();
                    arguments.Add(binaryExpr.Left);
                    arguments.Add(binaryExpr.Right);
                    CodeExpression targetObject = new CodeTypeReferenceExpression(method.DeclaringType);
                    analysis.AnalyzeRuleAttributes(method, targetObject, qualifier, arguments, method.GetParameters(), attributedExprs);
                }
            }
 
            // Analyze the left & right children.
            RuleExpressionWalker.AnalyzeUsage(analysis, binaryExpr.Left, true, false, null);
            RuleExpressionWalker.AnalyzeUsage(analysis, binaryExpr.Right, true, false, null);
        }
 
        #region Evaluate
        internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
        {
            CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
 
            object lhsValue = RuleExpressionWalker.Evaluate(execution, binaryExpr.Left).Value;
            CodeBinaryOperatorType operation = binaryExpr.Operator;
            // short-circuit ANDs and ORs
            if (operation == CodeBinaryOperatorType.BooleanAnd)
            {
                if ((bool)lhsValue)
                {
                    // LHS is true, need to look at RHS
                    object rhsValue = RuleExpressionWalker.Evaluate(execution, binaryExpr.Right).Value;
                    return new RuleLiteralResult(rhsValue);
                }
                else
                    // LHS is false, so result is false
                    return new RuleLiteralResult(false);
            }
            else if (operation == CodeBinaryOperatorType.BooleanOr)
            {
                if ((bool)lhsValue)
                    // LHS is true, so result is true
                    return new RuleLiteralResult(true);
                else
                {
                    // LHS is false, so need to look at RHS
                    object rhsValue = RuleExpressionWalker.Evaluate(execution, binaryExpr.Right).Value;
                    return new RuleLiteralResult(rhsValue);
                }
            }
            else
            {
                object resultValue;
                object rhsValue = RuleExpressionWalker.Evaluate(execution, binaryExpr.Right).Value;
                RuleBinaryExpressionInfo expressionInfo = execution.Validation.ExpressionInfo(binaryExpr) as RuleBinaryExpressionInfo;
                if (expressionInfo == null)  // Oops, someone forgot to validate.
                {
                    string message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
                    InvalidOperationException exception = new InvalidOperationException(message);
                    exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
                    throw exception;
                }
                MethodInfo methodInfo = expressionInfo.MethodInfo;
                if (methodInfo != null)
                {
                    if (methodInfo == Literal.ObjectEquality)
                    {
                        resultValue = (lhsValue == rhsValue);
                    }
                    else
                    {
                        ParameterInfo[] existingParameters = methodInfo.GetParameters();
                        object[] parameters = new object[2];
                        parameters[0] = Executor.AdjustType(expressionInfo.LeftType, lhsValue, existingParameters[0].ParameterType);
                        parameters[1] = Executor.AdjustType(expressionInfo.RightType, rhsValue, existingParameters[1].ParameterType);
                        resultValue = methodInfo.Invoke(null, parameters);
                    }
                }
                else
                {
                    resultValue = EvaluateBinaryOperation(binaryExpr, expressionInfo.LeftType, lhsValue, operation, expressionInfo.RightType, rhsValue);
                }
                return new RuleLiteralResult(resultValue);
            }
        }
 
        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        private static object EvaluateBinaryOperation(CodeBinaryOperatorExpression binaryExpr, Type lhsType, object lhsValue, CodeBinaryOperatorType operation, Type rhsType, object rhsValue)
        {
            Literal leftLiteral;
            Literal rightLiteral;
            ArithmeticLiteral leftArithmetic;
            ArithmeticLiteral rightArithmetic;
            string message;
            RuleEvaluationException exception;
 
            switch (operation)
            {
                case CodeBinaryOperatorType.Add:
                    leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
                    if (leftArithmetic == null)
                        break;
                    rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
                    if (rightArithmetic == null)
                        break;
                    return leftArithmetic.Add(rightArithmetic);
                case CodeBinaryOperatorType.Subtract:
                    leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
                    if (leftArithmetic == null)
                        break;
                    rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
                    if (rightArithmetic == null)
                        break;
                    return leftArithmetic.Subtract(rightArithmetic);
                case CodeBinaryOperatorType.Multiply:
                    leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
                    if (leftArithmetic == null)
                        break;
                    rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
                    if (rightArithmetic == null)
                        break;
                    return leftArithmetic.Multiply(rightArithmetic);
                case CodeBinaryOperatorType.Divide:
                    leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
                    if (leftArithmetic == null)
                        break;
                    rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
                    if (rightArithmetic == null)
                        break;
                    return leftArithmetic.Divide(rightArithmetic);
                case CodeBinaryOperatorType.Modulus:
                    leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
                    if (leftArithmetic == null)
                        break;
                    rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
                    if (rightArithmetic == null)
                        break;
                    return leftArithmetic.Modulus(rightArithmetic);
                case CodeBinaryOperatorType.BitwiseAnd:
                    leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
                    if (leftArithmetic == null)
                        break;
                    rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
                    if (rightArithmetic == null)
                        break;
                    return leftArithmetic.BitAnd(rightArithmetic);
                case CodeBinaryOperatorType.BitwiseOr:
                    leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
                    if (leftArithmetic == null)
                        break;
                    rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
                    if (rightArithmetic == null)
                        break;
                    return leftArithmetic.BitOr(rightArithmetic);
 
                case CodeBinaryOperatorType.ValueEquality:
                    leftLiteral = Literal.MakeLiteral(lhsType, lhsValue);
                    if (leftLiteral == null)
                        break;
                    rightLiteral = Literal.MakeLiteral(rhsType, rhsValue);
                    if (rightLiteral == null)
                        break;
                    return leftLiteral.Equal(rightLiteral);
                case CodeBinaryOperatorType.IdentityEquality:
                    return lhsValue == rhsValue;
                case CodeBinaryOperatorType.IdentityInequality:
                    return lhsValue != rhsValue;
 
                case CodeBinaryOperatorType.LessThan:
                    leftLiteral = Literal.MakeLiteral(lhsType, lhsValue);
                    if (leftLiteral == null)
                        break;
                    rightLiteral = Literal.MakeLiteral(rhsType, rhsValue);
                    if (rightLiteral == null)
                        break;
                    return leftLiteral.LessThan(rightLiteral);
                case CodeBinaryOperatorType.LessThanOrEqual:
                    leftLiteral = Literal.MakeLiteral(lhsType, lhsValue);
                    if (leftLiteral == null)
                        break;
                    rightLiteral = Literal.MakeLiteral(rhsType, rhsValue);
                    if (rightLiteral == null)
                        break;
                    return leftLiteral.LessThanOrEqual(rightLiteral);
                case CodeBinaryOperatorType.GreaterThan:
                    leftLiteral = Literal.MakeLiteral(lhsType, lhsValue);
                    if (leftLiteral == null)
                        break;
                    rightLiteral = Literal.MakeLiteral(rhsType, rhsValue);
                    if (rightLiteral == null)
                        break;
                    return leftLiteral.GreaterThan(rightLiteral);
                case CodeBinaryOperatorType.GreaterThanOrEqual:
                    leftLiteral = Literal.MakeLiteral(lhsType, lhsValue);
                    if (leftLiteral == null)
                        break;
                    rightLiteral = Literal.MakeLiteral(rhsType, rhsValue);
                    if (rightLiteral == null)
                        break;
                    return leftLiteral.GreaterThanOrEqual(rightLiteral);
 
                default:
                    // should never happen
                    // BooleanAnd & BooleanOr short-circuited before call
                    // Assign disallowed at validation time
                    message = string.Format(CultureInfo.CurrentCulture, Messages.BinaryOpNotSupported, operation.ToString());
                    exception = new RuleEvaluationException(message);
                    exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
                    throw exception;
            }
 
            message = string.Format(CultureInfo.CurrentCulture,
                Messages.BinaryOpFails,
                operation.ToString(),
                RuleDecompiler.DecompileType(lhsType),
                RuleDecompiler.DecompileType(rhsType));
            exception = new RuleEvaluationException(message);
            exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
            throw exception;
        }
        #endregion
 
        #region Decompile
 
        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
        {
            bool mustParenthesize = false;
            CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
 
            if (binaryExpr.Left == null)
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.NullBinaryOpLHS, binaryExpr.Operator.ToString());
                RuleEvaluationException exception = new RuleEvaluationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
                throw exception;
            }
            if (binaryExpr.Right == null)
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.NullBinaryOpRHS, binaryExpr.Operator.ToString());
                RuleEvaluationException exception = new RuleEvaluationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
                throw exception;
            }
 
            string opString;
 
            switch (binaryExpr.Operator)
            {
                case CodeBinaryOperatorType.Modulus:
                    opString = " % ";
                    break;
                case CodeBinaryOperatorType.Multiply:
                    opString = " * ";
                    break;
                case CodeBinaryOperatorType.Divide:
                    opString = " / ";
                    break;
 
                case CodeBinaryOperatorType.Subtract:
                    opString = " - ";
                    break;
                case CodeBinaryOperatorType.Add:
                    opString = " + ";
                    break;
 
                case CodeBinaryOperatorType.LessThan:
                    opString = " < ";
                    break;
                case CodeBinaryOperatorType.LessThanOrEqual:
                    opString = " <= ";
                    break;
                case CodeBinaryOperatorType.GreaterThan:
                    opString = " > ";
                    break;
                case CodeBinaryOperatorType.GreaterThanOrEqual:
                    opString = " >= ";
                    break;
 
                case CodeBinaryOperatorType.IdentityEquality:
                case CodeBinaryOperatorType.ValueEquality:
                    opString = " == ";
                    break;
                case CodeBinaryOperatorType.IdentityInequality:
                    opString = " != ";
                    break;
 
                case CodeBinaryOperatorType.BitwiseAnd:
                    opString = " & ";
                    break;
 
                case CodeBinaryOperatorType.BitwiseOr:
                    opString = " | ";
                    break;
 
                case CodeBinaryOperatorType.BooleanAnd:
                    opString = " && ";
                    break;
 
                case CodeBinaryOperatorType.BooleanOr:
                    opString = " || ";
                    break;
 
                default:
                    string message = string.Format(CultureInfo.CurrentCulture, Messages.BinaryOpNotSupported, binaryExpr.Operator.ToString());
                    NotSupportedException exception = new NotSupportedException(message);
                    exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
                    throw exception;
            }
 
            CodeExpression leftExpr = binaryExpr.Left;
            CodeExpression rightExpr = binaryExpr.Right;
 
 
            if (binaryExpr.Operator == CodeBinaryOperatorType.ValueEquality)
            {
                // Look for special cases:
                //    LHS == false              --> ! LHS
                // or
                //    (LHS == expr) == false    --> LHS != expr
 
                CodePrimitiveExpression rhsPrimitive = rightExpr as CodePrimitiveExpression;
                if (rhsPrimitive != null)
                {
                    object rhsValue = rhsPrimitive.Value;
                    if (rhsValue != null)
                    {
                        // we don't have the comparison "==null"
                        if (rhsValue.GetType() == typeof(bool) && (bool)rhsValue == false)
                        {
                            // We have comparison "== false".
 
                            CodeBinaryOperatorExpression lhsBinary = leftExpr as CodeBinaryOperatorExpression;
                            if (lhsBinary != null && lhsBinary.Operator == CodeBinaryOperatorType.ValueEquality)
                            {
                                // We have the pattern
                                //      (expr1 == expr2) == false
                                // Treat this as:
                                //      expr1 != expr2
 
                                opString = " != ";
 
                                leftExpr = lhsBinary.Left;
                                rightExpr = lhsBinary.Right;
                            }
                            else
                            {
                                // We have the pattern
                                //      LHS == false
                                // Treat this as:
                                //      ! LHS
 
                                mustParenthesize = RuleDecompiler.MustParenthesize(leftExpr, parentExpression);
                                if (mustParenthesize)
                                    stringBuilder.Append("(");
 
                                // Note the "parentExpression" passed to the child decompile... cast is the only
                                // built-in operation that has "unary" precedence, so pass that as the parent
                                // to get the parenthesization right. .
                                stringBuilder.Append("!");
                                RuleExpressionWalker.Decompile(stringBuilder, leftExpr, new CodeCastExpression());
 
                                if (mustParenthesize)
                                    stringBuilder.Append(")");
 
                                return;
                            }
                        }
                    }
                }
            }
            else if (binaryExpr.Operator == CodeBinaryOperatorType.Subtract)
            {
                // Look for the special case:
                //    0 - RHS       --> - RHS
 
                CodePrimitiveExpression lhsPrimitive = leftExpr as CodePrimitiveExpression;
                if (lhsPrimitive != null && lhsPrimitive.Value != null)
                {
                    object lhsValue = lhsPrimitive.Value;
 
                    // Check if the LHS is zero.  We'll only check a few types (decimal,
                    // double, float, int, long), since these occur most often (and the 
                    // unsigned types are all illegal).
                    TypeCode tc = Type.GetTypeCode(lhsValue.GetType());
                    bool isZero = false;
                    switch (tc)
                    {
                        case TypeCode.Decimal:
                            isZero = ((decimal)lhsValue) == 0;
                            break;
 
                        case TypeCode.Double:
                            isZero = ((double)lhsValue) == 0;
                            break;
 
                        case TypeCode.Single:
                            isZero = ((float)lhsValue) == 0;
                            break;
 
                        case TypeCode.Int32:
                            isZero = ((int)lhsValue) == 0;
                            break;
 
                        case TypeCode.Int64:
                            isZero = ((long)lhsValue) == 0;
                            break;
                    }
 
                    if (isZero)
                    {
                        mustParenthesize = RuleDecompiler.MustParenthesize(rightExpr, parentExpression);
                        if (mustParenthesize)
                            stringBuilder.Append("(");
 
                        // Note the "parentExpression" passed to the child decompile... cast is the only
                        // built-in operation that has "unary" precedence, so pass that as the parent
                        // to get the parenthesization right.  
                        stringBuilder.Append("-");
                        RuleExpressionWalker.Decompile(stringBuilder, rightExpr, new CodeCastExpression());
 
                        if (mustParenthesize)
                            stringBuilder.Append(")");
 
                        return;
                    }
                }
            }
 
            mustParenthesize = RuleDecompiler.MustParenthesize(binaryExpr, parentExpression);
            if (mustParenthesize)
                stringBuilder.Append("(");
 
            RuleExpressionWalker.Decompile(stringBuilder, leftExpr, binaryExpr);
            stringBuilder.Append(opString);
            RuleExpressionWalker.Decompile(stringBuilder, rightExpr, binaryExpr);
 
            if (mustParenthesize)
                stringBuilder.Append(")");
        }
        #endregion
 
        internal override CodeExpression Clone(CodeExpression expression)
        {
            CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
 
            CodeBinaryOperatorExpression newOp = new CodeBinaryOperatorExpression();
            newOp.Operator = binaryExpr.Operator;
            newOp.Left = RuleExpressionWalker.Clone(binaryExpr.Left);
            newOp.Right = RuleExpressionWalker.Clone(binaryExpr.Right);
            return newOp;
        }
 
        internal override bool Match(CodeExpression expression, CodeExpression comperand)
        {
            CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
 
            CodeBinaryOperatorExpression comperandBinary = (CodeBinaryOperatorExpression)comperand;
            return (binaryExpr.Operator == comperandBinary.Operator
                && RuleExpressionWalker.Match(binaryExpr.Left, comperandBinary.Left)
                && RuleExpressionWalker.Match(binaryExpr.Right, comperandBinary.Right));
        }
    }
 
    #endregion
 
    #region Field ref expression
 
    // CodeFieldReferenceExpression
    internal class FieldReferenceExpression : RuleExpressionInternal
    {
        internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
        {
            string message;
 
            CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
 
            if (fieldRefExpr.TargetObject == null)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.NullFieldTarget, fieldRefExpr.FieldName);
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            // Early exit from this if a cycle is detected.
            if (!validation.PushParentExpression(fieldRefExpr))
                return null;
 
            RuleExpressionInfo targetExprInfo = RuleExpressionWalker.Validate(validation, fieldRefExpr.TargetObject, false);
 
            validation.PopParentExpression();
 
            if (targetExprInfo == null)     // error occurred, so simply return
                return null;
 
            Type targetType = targetExprInfo.ExpressionType;
            if (targetType == null)         // no type, so must have been an error already
                return null;
 
            if (targetType == typeof(NullLiteral))
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.NullFieldTarget, fieldRefExpr.FieldName);
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_BindingTypeMissing);
                error.UserData[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            BindingFlags bindingFlags = BindingFlags.Public;
            if (fieldRefExpr.TargetObject is CodeTypeReferenceExpression)
                bindingFlags |= BindingFlags.Static | BindingFlags.FlattenHierarchy;
            else
                bindingFlags |= BindingFlags.Instance;
            if (validation.AllowInternalMembers(targetType))
                bindingFlags |= BindingFlags.NonPublic;
 
            FieldInfo fi = targetType.GetField(fieldRefExpr.FieldName, bindingFlags);
            if (fi == null)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownField, fieldRefExpr.FieldName, RuleDecompiler.DecompileType(targetType));
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
                error.UserData[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            if (fi.FieldType == null)
            {
                // This can only happen with a design-time type.
                message = string.Format(CultureInfo.CurrentCulture, Messages.CouldNotDetermineMemberType, fieldRefExpr.FieldName);
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_CouldNotDetermineMemberType);
                error.UserData[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            if (isWritten && fi.IsLiteral)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.FieldSetNotAllowed, fieldRefExpr.FieldName, RuleDecompiler.DecompileType(targetType));
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
                error.UserData[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            if (!validation.ValidateMemberAccess(fieldRefExpr.TargetObject, targetType, fi, fi.Name, fieldRefExpr))
                return null;
 
            // Is it possible to set fi by validation.ResolveFieldOrProperty(targetType, fieldExpr.FieldName)?
            validation.IsAuthorized(fi.FieldType);
            return new RuleFieldExpressionInfo(fi);
        }
 
        internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
        {
            CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
            CodeExpression targetObject = fieldRefExpr.TargetObject;
            RuleExpressionWalker.AnalyzeUsage(analysis, targetObject, isRead, isWritten, new RulePathQualifier(fieldRefExpr.FieldName, qualifier));
        }
 
        internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
        {
            CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
            object target = RuleExpressionWalker.Evaluate(execution, fieldRefExpr.TargetObject).Value;
 
            RuleFieldExpressionInfo fieldExprInfo = execution.Validation.ExpressionInfo(fieldRefExpr) as RuleFieldExpressionInfo;
            if (fieldExprInfo == null)  // Oops, someone forgot to validate.
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
                InvalidOperationException exception = new InvalidOperationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
                throw exception;
            }
 
            FieldInfo fi = fieldExprInfo.FieldInfo;
 
            return new RuleFieldResult(target, fi);
        }
 
        internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
        {
            CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
 
            CodeExpression targetObject = fieldRefExpr.TargetObject;
            if (targetObject == null)
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.NullFieldTarget, fieldRefExpr.FieldName);
                RuleEvaluationException exception = new RuleEvaluationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
                throw exception;
            }
 
            RuleExpressionWalker.Decompile(stringBuilder, targetObject, fieldRefExpr);
            stringBuilder.Append('.');
            stringBuilder.Append(fieldRefExpr.FieldName);
        }
 
        internal override CodeExpression Clone(CodeExpression expression)
        {
            CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
 
            CodeFieldReferenceExpression newField = new CodeFieldReferenceExpression();
            newField.FieldName = fieldRefExpr.FieldName;
            newField.TargetObject = RuleExpressionWalker.Clone(fieldRefExpr.TargetObject);
            return newField;
        }
 
        internal override bool Match(CodeExpression expression, CodeExpression comperand)
        {
            CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
 
            CodeFieldReferenceExpression newField = (CodeFieldReferenceExpression)comperand;
            return (fieldRefExpr.FieldName == newField.FieldName
                && RuleExpressionWalker.Match(fieldRefExpr.TargetObject, newField.TargetObject));
        }
    }
 
    #endregion
 
    #region Property ref expression
 
    // CodePropertyReferenceExpression
    internal class PropertyReferenceExpression : RuleExpressionInternal
    {
        internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
        {
            string message;
 
            CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
 
            if (propGetExpr.TargetObject == null)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.NullPropertyTarget, propGetExpr.PropertyName);
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = propGetExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            // Early exit from this if a cycle is detected.
            if (!validation.PushParentExpression(propGetExpr))
                return null;
 
            RuleExpressionInfo targetExprInfo = RuleExpressionWalker.Validate(validation, propGetExpr.TargetObject, false);
 
            validation.PopParentExpression();
 
            if (targetExprInfo == null)     // error occurred, so simply return
                return null;
 
            Type targetType = targetExprInfo.ExpressionType;
            if (targetType == null)         // no type, so must have been an error already
                return null;
 
            if (targetType == typeof(NullLiteral))
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.NullPropertyTarget, propGetExpr.PropertyName);
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_BindingTypeMissing);
                error.UserData[RuleUserDataKeys.ErrorObject] = propGetExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            bool includeNonPublic = false;
            BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy;
            if (validation.AllowInternalMembers(targetType))
            {
                bindingFlags |= BindingFlags.NonPublic;
                includeNonPublic = true;
            }
            PropertyInfo pi = validation.ResolveProperty(targetType, propGetExpr.PropertyName, bindingFlags);
 
            if (pi == null)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownProperty, propGetExpr.PropertyName, RuleDecompiler.DecompileType(targetType));
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
                error.UserData[RuleUserDataKeys.ErrorObject] = propGetExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            if (pi.PropertyType == null)
            {
                // This can only happen with a design-time type.
                message = string.Format(CultureInfo.CurrentCulture, Messages.CouldNotDetermineMemberType, propGetExpr.PropertyName);
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_CouldNotDetermineMemberType);
                error.UserData[RuleUserDataKeys.ErrorObject] = propGetExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            MethodInfo accessorMethod = isWritten ? pi.GetSetMethod(includeNonPublic) : pi.GetGetMethod(includeNonPublic);
            if (accessorMethod == null)
            {
                string baseMessage = isWritten ? Messages.UnknownPropertySet : Messages.UnknownPropertyGet;
                message = string.Format(CultureInfo.CurrentCulture, baseMessage, propGetExpr.PropertyName, RuleDecompiler.DecompileType(targetType));
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
                error.UserData[RuleUserDataKeys.ErrorObject] = propGetExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            if (!validation.ValidateMemberAccess(propGetExpr.TargetObject, targetType, accessorMethod, propGetExpr.PropertyName, propGetExpr))
                return null;
 
            // Validate any RuleAttributes, if present.
            object[] attrs = pi.GetCustomAttributes(typeof(RuleAttribute), true);
            if (attrs != null && attrs.Length > 0)
            {
                Stack<MemberInfo> methodStack = new Stack<MemberInfo>();
                methodStack.Push(pi);
 
                bool allAttributesValid = true;
                foreach (RuleAttribute ruleAttr in attrs)
                {
                    if (!ruleAttr.Validate(validation, pi, targetType, null))
                        allAttributesValid = false;
                }
 
                methodStack.Pop();
 
                if (!allAttributesValid)
                    return null;
            }
 
            return new RulePropertyExpressionInfo(pi, pi.PropertyType, false);
        }
 
 
        internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
        {
            string message;
 
            CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
 
            // Evaluate the target object and get its type.
            CodeExpression targetObject = propGetExpr.TargetObject;
            RuleExpressionInfo targetExprInfo = analysis.Validation.ExpressionInfo(targetObject);
            if (targetExprInfo == null)  // Oops, someone forgot to validate.
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
                InvalidOperationException exception = new InvalidOperationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = targetObject;
                throw exception;
            }
 
            // Get the property info from the validator so we can look for [RuleRead] and [RuleWrite] attributes.
            RulePropertyExpressionInfo propExprInfo = analysis.Validation.ExpressionInfo(propGetExpr) as RulePropertyExpressionInfo;
            if (propExprInfo == null)  // Oops, someone forgot to validate.
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
                InvalidOperationException exception = new InvalidOperationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = propGetExpr;
                throw exception;
            }
 
            PropertyInfo pi = propExprInfo.PropertyInfo;
 
            // Look for RuleAttribute's on the invoked property.
            List<CodeExpression> attributedExprs = new List<CodeExpression>();
            analysis.AnalyzeRuleAttributes(pi, targetObject, qualifier, null, null, attributedExprs);
 
            // See if the target object needs default analysis.
            if (!attributedExprs.Contains(targetObject))
            {
                // The property had no [RuleRead] or [RuleWrite] attributes.  Just qualify the target object with
                // the property name and proceed with the analysis.
                RuleExpressionWalker.AnalyzeUsage(analysis, targetObject, isRead, isWritten, new RulePathQualifier(pi.Name, qualifier));
            }
        }
 
        internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
        {
            CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
 
            object target = RuleExpressionWalker.Evaluate(execution, propGetExpr.TargetObject).Value;
 
            RulePropertyExpressionInfo propExprInfo = execution.Validation.ExpressionInfo(propGetExpr) as RulePropertyExpressionInfo;
            if (propExprInfo == null)  // Oops, someone forgot to validate.
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
                InvalidOperationException exception = new InvalidOperationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = propGetExpr;
                throw exception;
            }
 
            PropertyInfo pi = propExprInfo.PropertyInfo;
            return new RulePropertyResult(pi, target, null);
        }
 
        internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
        {
            CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
 
            CodeExpression targetObject = propGetExpr.TargetObject;
            if (targetObject == null)
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.NullPropertyTarget, propGetExpr.PropertyName);
                RuleEvaluationException exception = new RuleEvaluationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = propGetExpr;
                throw exception;
            }
 
            RuleExpressionWalker.Decompile(stringBuilder, targetObject, propGetExpr);
            stringBuilder.Append('.');
            stringBuilder.Append(propGetExpr.PropertyName);
        }
 
        internal override CodeExpression Clone(CodeExpression expression)
        {
            CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
 
            CodePropertyReferenceExpression newProperty = new CodePropertyReferenceExpression();
            newProperty.PropertyName = propGetExpr.PropertyName;
            newProperty.TargetObject = RuleExpressionWalker.Clone(propGetExpr.TargetObject);
            return newProperty;
        }
 
        internal override bool Match(CodeExpression expression, CodeExpression comperand)
        {
            CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
 
            CodePropertyReferenceExpression newProperty = (CodePropertyReferenceExpression)comperand;
            return (propGetExpr.PropertyName == newProperty.PropertyName
                && RuleExpressionWalker.Match(propGetExpr.TargetObject, newProperty.TargetObject));
        }
    }
 
    #endregion
 
    #region Method invoke expression
 
    // CodeMethodInvokeExpression
    internal class MethodInvokeExpression : RuleExpressionInternal
    {
        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
        {
            Type targetType = null;
            RuleMethodInvokeExpressionInfo methodInvokeInfo = null;
            string message;
            ValidationError error = null;
            BindingFlags bindingFlags = BindingFlags.Public;
 
            CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
 
            if (isWritten)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeMethodInvokeExpression).ToString());
                error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
                error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            if ((invokeExpr.Method == null) || (invokeExpr.Method.TargetObject == null))
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.NullMethodTarget, invokeExpr.Method.MethodName);
                error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
                validation.Errors.Add(error);
                return null; // Fatal error; discontinue validation of this object.
            }
 
            if ((invokeExpr.Method.TypeArguments != null) && (invokeExpr.Method.TypeArguments.Count > 0))
            {
                error = new ValidationError(Messages.GenericMethodsNotSupported, ErrorNumbers.Error_CodeExpressionNotHandled);
                error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            try
            {
                // Early exit from this if a cycle is detected.
                if (!validation.PushParentExpression(invokeExpr))
                    return null;
 
                RuleExpressionInfo targetExprInfo = RuleExpressionWalker.Validate(validation, invokeExpr.Method.TargetObject, false);
                if (targetExprInfo == null)     // error occurred, so simply return
                    return null;
 
                targetType = targetExprInfo.ExpressionType;
                if (targetType == null)
                    return null;
 
                // if an error occurred (targetType == null), continue on to validate the arguments
                if (targetType == typeof(NullLiteral))
                {
                    message = string.Format(CultureInfo.CurrentCulture, Messages.NullMethodTarget, invokeExpr.Method.MethodName);
                    error = new ValidationError(message, ErrorNumbers.Error_BindingTypeMissing);
                    error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
                    validation.Errors.Add(error);
                    targetType = null; // force exit after validating the arguments
                }
 
                List<CodeExpression> argExprs = new List<CodeExpression>();
 
                bool hasInvalidArgument = false;
                if (invokeExpr.Parameters != null)
                {
                    for (int i = 0; i < invokeExpr.Parameters.Count; ++i)
                    {
                        CodeExpression argExpr = invokeExpr.Parameters[i];
                        if (argExpr == null)
                        {
                            message = string.Format(CultureInfo.CurrentCulture, Messages.NullMethodParameter, i.ToString(CultureInfo.CurrentCulture), invokeExpr.Method.MethodName);
                            error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
                            error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
                            validation.Errors.Add(error);
                            targetType = null; // force exit after validating the rest of the arguments
                        }
                        else
                        {
                            if (argExpr is CodeTypeReferenceExpression)
                            {
                                message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, argExpr.GetType().FullName);
                                error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
                                error.UserData[RuleUserDataKeys.ErrorObject] = argExpr;
                                validation.AddError(error);
 
                                hasInvalidArgument = true;
                            }
 
                            // Validate the argument.
                            RuleExpressionInfo argExprInfo = RuleExpressionWalker.Validate(validation, argExpr, false);
                            if (argExprInfo == null)
                                hasInvalidArgument = true;
                            argExprs.Add(argExpr);
                        }
                    }
                }
 
                // Stop further validation if there was a problem with the target expression.
                if (targetType == null)
                    return null;
 
                // Stop further validation if there was a problem with any of the arguments.
                if (hasInvalidArgument)
                    return null;
 
                if (invokeExpr.Method.TargetObject is CodeTypeReferenceExpression)
                    bindingFlags |= BindingFlags.Static | BindingFlags.FlattenHierarchy;
                else
                    bindingFlags |= BindingFlags.Instance;
                if (validation.AllowInternalMembers(targetType))
                    bindingFlags |= BindingFlags.NonPublic;
 
                // Everything okay so far, try to resolve the method.
                methodInvokeInfo = validation.ResolveMethod(targetType, invokeExpr.Method.MethodName, bindingFlags, argExprs, out error);
                if ((methodInvokeInfo == null) && (invokeExpr.UserData.Contains(RuleUserDataKeys.QualifiedName)))
                {
                    // failed to resolve the method, but a fully qualified type name is around
                    // load the type, add it to the assemblies, and try again
                    string qualifiedName = invokeExpr.UserData[RuleUserDataKeys.QualifiedName] as string;
                    Type containingClassType = validation.ResolveType(qualifiedName);
                    if (containingClassType != null)
                    {
                        validation.DetermineExtensionMethods(containingClassType.Assembly);
                        methodInvokeInfo = validation.ResolveMethod(targetType, invokeExpr.Method.MethodName, bindingFlags, argExprs, out error);
                    }
                }
                if (methodInvokeInfo == null)
                {
                    error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
                    validation.Errors.Add(error);
                    return null;
                }
            }
            finally
            {
                validation.PopParentExpression();
            }
 
 
            MethodInfo mi = methodInvokeInfo.MethodInfo;
 
            if (mi.ReturnType == null)
            {
                // This can only happen with a design-time type.
                message = string.Format(CultureInfo.CurrentCulture, Messages.CouldNotDetermineMemberType, invokeExpr.Method.MethodName);
                error = new ValidationError(message, ErrorNumbers.Error_CouldNotDetermineMemberType);
                error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            if (!validation.ValidateMemberAccess(invokeExpr.Method.TargetObject, targetType, mi, invokeExpr.Method.MethodName, invokeExpr))
                return null;
 
            // Validate any RuleAttributes, if present.
            object[] attrs = mi.GetCustomAttributes(typeof(RuleAttribute), true);
            if (attrs != null && attrs.Length > 0)
            {
                Stack<MemberInfo> methodStack = new Stack<MemberInfo>();
                methodStack.Push(mi);
 
                bool allAttributesValid = true;
                foreach (RuleAttribute ruleAttr in attrs)
                {
                    if (!ruleAttr.Validate(validation, mi, targetType, mi.GetParameters()))
                        allAttributesValid = false;
                }
 
                methodStack.Pop();
 
                if (!allAttributesValid)
                    return null;
            }
 
            // if this is an extension method, save the type information
            if (mi is ExtensionMethodInfo)
            {
                invokeExpr.UserData[RuleUserDataKeys.QualifiedName] = mi.DeclaringType.AssemblyQualifiedName;
            }
 
            return methodInvokeInfo;
        }
 
        internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
        {
            string message;
 
            CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
 
            // Get the target object's type.
            CodeExpression targetObject = invokeExpr.Method.TargetObject;
            RuleExpressionInfo targetExprInfo = analysis.Validation.ExpressionInfo(targetObject);
            if (targetExprInfo == null)  // Oops, someone forgot to validate.
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
                InvalidOperationException exception = new InvalidOperationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = targetObject;
                throw exception;
            }
 
            // Get the method info from the validation so we can look for [RuleRead] and [RuleWrite] attributes.
            RuleMethodInvokeExpressionInfo methodExprInfo = analysis.Validation.ExpressionInfo(invokeExpr) as RuleMethodInvokeExpressionInfo;
            if (methodExprInfo == null)  // Oops, someone forgot to validate.
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
                InvalidOperationException exception = new InvalidOperationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = invokeExpr;
                throw exception;
            }
 
            MethodInfo mi = methodExprInfo.MethodInfo;
 
            // Look for RuleAttribute's on the invoked method.
            List<CodeExpression> attributedExprs = new List<CodeExpression>();
            analysis.AnalyzeRuleAttributes(mi, targetObject, qualifier, invokeExpr.Parameters, mi.GetParameters(), attributedExprs);
 
            // See if the target object needs default analysis.
            if (!attributedExprs.Contains(targetObject))
            {
                // No applicable [RuleRead] or [RuleWrite] attributes were found on the target object.
 
                // If we're analyzing for dependencies, assume that this method uses the
                // value of the target object, but nothing beneath it.
 
                RuleExpressionWalker.AnalyzeUsage(analysis, targetObject, true, false, null);
            }
 
            // See if any of the arguments need default analysis.
            for (int i = 0; i < invokeExpr.Parameters.Count; ++i)
            {
                CodeExpression argExpr = invokeExpr.Parameters[i];
 
                if (!attributedExprs.Contains(argExpr))
                {
                    // Similar to the target object, we assume that this method can reads the value
                    // of the parameter, but none of its members.
                    RuleExpressionWalker.AnalyzeUsage(analysis, argExpr, true, false, null);
                }
            }
        }
 
        internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
        {
            string message;
 
            CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
 
            object target = RuleExpressionWalker.Evaluate(execution, invokeExpr.Method.TargetObject).Value;
 
            RuleMethodInvokeExpressionInfo invokeExprInfo = execution.Validation.ExpressionInfo(invokeExpr) as RuleMethodInvokeExpressionInfo;
            if (invokeExprInfo == null)  // Oops, someone forgot to validate.
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
                InvalidOperationException exception = new InvalidOperationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = invokeExpr;
                throw exception;
            }
 
            MethodInfo mi = invokeExprInfo.MethodInfo;
 
            if (!mi.IsStatic && target == null)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.TargetEvaluatedNullMethod, invokeExpr.Method.MethodName);
                RuleEvaluationException exception = new RuleEvaluationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = invokeExpr;
                throw exception;
            }
 
            object[] arguments = null;
            RuleExpressionResult[] outArgumentResults = null;
 
            if (invokeExpr.Parameters != null && invokeExpr.Parameters.Count > 0)
            {
                int actualArgCount = invokeExpr.Parameters.Count;
 
                ParameterInfo[] parmInfos = mi.GetParameters();
 
                arguments = new object[parmInfos.Length];
 
                int numFixedParameters = parmInfos.Length;
                if (invokeExprInfo.NeedsParamsExpansion)
                    numFixedParameters -= 1;
 
                int i;
 
                // Evaluate the fixed portion of the parameter list.
                for (i = 0; i < numFixedParameters; ++i)
                {
                    Type argType = execution.Validation.ExpressionInfo(invokeExpr.Parameters[i]).ExpressionType;
                    RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, invokeExpr.Parameters[i]);
 
                    // Special procesing of direction expressions to keep track of out arguments (& ref).
                    CodeDirectionExpression direction = invokeExpr.Parameters[i] as CodeDirectionExpression;
                    if (direction != null && (direction.Direction == FieldDirection.Ref || direction.Direction == FieldDirection.Out))
                    {
                        // lazy creation of fieldsToSet
                        if (outArgumentResults == null)
                            outArgumentResults = new RuleExpressionResult[invokeExpr.Parameters.Count];
                        // keep track of this out expression so we can set it later
                        outArgumentResults[i] = argResult;
 
                        // don't evaluate out arguments
                        if (direction.Direction != FieldDirection.Out)
                            arguments[i] = Executor.AdjustType(argType, argResult.Value, parmInfos[i].ParameterType);
                    }
                    else
                    {
                        // treat as in
                        arguments[i] = Executor.AdjustType(argType, argResult.Value, parmInfos[i].ParameterType);
                    }
                }
 
                if (numFixedParameters < actualArgCount)
                {
                    // This target method had a params array, and we are calling it with an
                    // expanded parameter list.  E.g.,
                    //      void foo(int x, params string[] y)
                    // with the invocation:
                    //      foo(5, "crud", "kreeble", "glorp")
                    // We need to translate this to:
                    //      foo(5, new string[] { "crud", "kreeble", "glorp" })
 
                    ParameterInfo lastParamInfo = parmInfos[numFixedParameters];
 
                    Type arrayType = lastParamInfo.ParameterType;
                    System.Diagnostics.Debug.Assert(arrayType.IsArray);
                    Type elementType = arrayType.GetElementType();
 
                    Array paramsArray = (Array)arrayType.InvokeMember(arrayType.Name, BindingFlags.CreateInstance, null, null, new object[] { actualArgCount - i }, CultureInfo.CurrentCulture);
                    for (; i < actualArgCount; ++i)
                    {
                        Type argType = execution.Validation.ExpressionInfo(invokeExpr.Parameters[i]).ExpressionType;
                        RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, invokeExpr.Parameters[i]);
                        paramsArray.SetValue(Executor.AdjustType(argType, argResult.Value, elementType), i - numFixedParameters);
                    }
 
                    arguments[numFixedParameters] = paramsArray;
                }
            }
 
            object result;
            try
            {
                result = mi.Invoke(target, arguments);
            }
            catch (TargetInvocationException e)
            {
                // if there is no inner exception, leave it untouched
                if (e.InnerException == null)
                    throw;
                message = string.Format(CultureInfo.CurrentCulture, Messages.Error_MethodInvoke,
                    RuleDecompiler.DecompileType(mi.ReflectedType), mi.Name, e.InnerException.Message);
                throw new TargetInvocationException(message, e.InnerException);
            }
 
            // any out/ref parameters that need to be assigned?
            if (outArgumentResults != null)
            {
                for (int i = 0; i < invokeExpr.Parameters.Count; ++i)
                {
                    if (outArgumentResults[i] != null)
                        outArgumentResults[i].Value = arguments[i];
                }
            }
 
            return new RuleLiteralResult(result);
        }
 
        internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
        {
            CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
 
            if ((invokeExpr.Method == null) || (invokeExpr.Method.TargetObject == null))
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.NullMethodTarget, invokeExpr.Method.MethodName);
                RuleEvaluationException exception = new RuleEvaluationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = invokeExpr;
                throw exception;
            }
 
            // Decompile the target expression.
            CodeExpression targetObject = invokeExpr.Method.TargetObject;
            RuleExpressionWalker.Decompile(stringBuilder, targetObject, invokeExpr);
 
            stringBuilder.Append('.');
            stringBuilder.Append(invokeExpr.Method.MethodName);
 
            // Decompile the arguments
            stringBuilder.Append('(');
 
            if (invokeExpr.Parameters != null)
            {
                for (int i = 0; i < invokeExpr.Parameters.Count; ++i)
                {
                    CodeExpression paramExpr = invokeExpr.Parameters[i];
                    if (paramExpr == null)
                    {
                        string message = string.Format(CultureInfo.CurrentCulture, Messages.NullMethodTypeParameter, i.ToString(CultureInfo.CurrentCulture), invokeExpr.Method.MethodName);
                        RuleEvaluationException exception = new RuleEvaluationException(message);
                        exception.Data[RuleUserDataKeys.ErrorObject] = invokeExpr;
                        throw exception;
                    }
 
                    if (i > 0)
                        stringBuilder.Append(", ");
 
                    RuleExpressionWalker.Decompile(stringBuilder, paramExpr, null);
                }
            }
 
            stringBuilder.Append(')');
        }
 
        internal override CodeExpression Clone(CodeExpression expression)
        {
            CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
 
            CodeMethodInvokeExpression newMethod = new CodeMethodInvokeExpression();
            newMethod.Method = CloneMethodReference(invokeExpr.Method);
            foreach (CodeExpression argument in invokeExpr.Parameters)
                newMethod.Parameters.Add(RuleExpressionWalker.Clone(argument));
            return newMethod;
        }
 
        private static CodeMethodReferenceExpression CloneMethodReference(CodeMethodReferenceExpression oldReference)
        {
            CodeMethodReferenceExpression newReference = new CodeMethodReferenceExpression();
            newReference.MethodName = oldReference.MethodName;
            newReference.TargetObject = RuleExpressionWalker.Clone(oldReference.TargetObject);
            foreach (CodeTypeReference typeReference in oldReference.TypeArguments)
                newReference.TypeArguments.Add(TypeReferenceExpression.CloneType(typeReference));
            ConditionHelper.CloneUserData(oldReference, newReference);
            return newReference;
        }
 
        internal override bool Match(CodeExpression expression, CodeExpression comperand)
        {
            CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
 
            CodeMethodInvokeExpression newMethod = (CodeMethodInvokeExpression)comperand;
            if (invokeExpr.Method.MethodName != newMethod.Method.MethodName)
                return false;
            if (!RuleExpressionWalker.Match(invokeExpr.Method.TargetObject, newMethod.Method.TargetObject))
                return false;
            if (invokeExpr.Parameters.Count != newMethod.Parameters.Count)
                return false;
            for (int i = 0; i < invokeExpr.Parameters.Count; ++i)
            {
                if (!RuleExpressionWalker.Match(invokeExpr.Parameters[i], newMethod.Parameters[i]))
                    return false;
            }
            return true;
        }
    }
 
    #endregion
 
    #region Direction expression (in/out/ref)
 
    // CodeDirectionExpression
    internal class DirectionExpression : RuleExpressionInternal
    {
        internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
        {
            CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
 
            if (isWritten)
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeDirectionExpression).ToString());
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
                error.UserData[RuleUserDataKeys.ErrorObject] = directionExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            // direction specified, make sure that something is specified
            if (directionExpr.Expression == null)
            {
                ValidationError error = new ValidationError(Messages.NullDirectionTarget, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = directionExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            if (directionExpr.Expression is CodeTypeReferenceExpression)
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, directionExpr.Expression.GetType().FullName);
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
                error.UserData[RuleUserDataKeys.ErrorObject] = directionExpr.Expression;
                validation.AddError(error);
                return null;
            }
 
            // validate the parameter
            RuleExpressionInfo paramExprInfo;
            bool isRef;
            if (directionExpr.Direction == FieldDirection.Ref)
            {
                // ref parameters require that we both read and write the value
                isRef = true;
                paramExprInfo = RuleExpressionWalker.Validate(validation, directionExpr.Expression, false);
                if (paramExprInfo == null)
                    return null;
                paramExprInfo = RuleExpressionWalker.Validate(validation, directionExpr.Expression, true);
            }
            else if (directionExpr.Direction == FieldDirection.Out)
            {
                // out parameters mean that we only write to it
                isRef = true;
                paramExprInfo = RuleExpressionWalker.Validate(validation, directionExpr.Expression, true);
            }
            else
            {
                // other parameters are treated as in, so we need to be able to read them
                isRef = false;
                paramExprInfo = RuleExpressionWalker.Validate(validation, directionExpr.Expression, false);
            }
            if (paramExprInfo == null)
                return null;
 
            // determine it's type
            Type parameterType = paramExprInfo.ExpressionType;
            if (parameterType == null)
                return null;
 
            if (parameterType != typeof(NullLiteral))
            {
                // adjust type if necessary
                if (isRef && !parameterType.IsByRef)
                    parameterType = parameterType.MakeByRefType();
            }
            return new RuleExpressionInfo(parameterType);
        }
 
        internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
        {
            CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
            CodeExpression paramExpr = directionExpr.Expression;
 
            bool argIsWritten = false;
            bool argIsRead = true;
            RulePathQualifier argQualifier = null;
            switch (directionExpr.Direction)
            {
                case FieldDirection.In:
                    // We assume that all children (* suffix) of this argument can be read.
                    argIsWritten = false;
                    argIsRead = true;
                    argQualifier = new RulePathQualifier("*", null);
                    break;
 
                case FieldDirection.Ref:
                    // When this happens in a condition, we treat this like an "in" (above): all
                    // children (* suffix) of this argument are read.  When this happens in an
                    // action, we treat this like an "out" (below):  we assume this argument is
                    // modified (no suffix).
                    argIsWritten = true;
                    argIsRead = true;
                    argQualifier = analysis.ForWrites ? null : new RulePathQualifier("*", null);
                    break;
 
                case FieldDirection.Out:
                    // We assume that this argument is modified (no suffix).
                    argIsWritten = true;
                    argIsRead = false;
                    argQualifier = null;
                    break;
            }
 
            RuleExpressionWalker.AnalyzeUsage(analysis, paramExpr, argIsRead, argIsWritten, argQualifier);
        }
 
        internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
        {
            // For evaluation purposes, ignore the direction.  It is handled specifically in the
            // method invoke Evaluate method.
            CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
            return RuleExpressionWalker.Evaluate(execution, directionExpr.Expression);
        }
 
        internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
        {
            CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
 
            string direction = null;
            if (directionExpr.Direction == FieldDirection.Out)
                direction = "out ";
            else if (directionExpr.Direction == FieldDirection.Ref)
                direction = "ref ";
 
            if (direction != null)
                stringBuilder.Append(direction);
 
            RuleExpressionWalker.Decompile(stringBuilder, directionExpr.Expression, directionExpr);
        }
 
        internal override CodeExpression Clone(CodeExpression expression)
        {
            CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
            CodeDirectionExpression newDirection = new CodeDirectionExpression();
            newDirection.Direction = directionExpr.Direction;
            newDirection.Expression = RuleExpressionWalker.Clone(directionExpr.Expression);
            return newDirection;
        }
 
        internal override bool Match(CodeExpression expression, CodeExpression comperand)
        {
            CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
            CodeDirectionExpression newDirection = (CodeDirectionExpression)comperand;
            return (directionExpr.Direction == newDirection.Direction &&
                RuleExpressionWalker.Match(directionExpr.Expression, newDirection.Expression));
        }
    }
 
    #endregion
 
    #region Type Reference expression
 
    // CodeTypeReferenceExpression
    internal class TypeReferenceExpression : RuleExpressionInternal
    {
        internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
        {
            CodeTypeReferenceExpression typeRefExpr = (CodeTypeReferenceExpression)expression;
 
            if (typeRefExpr.Type == null)
            {
                ValidationError error = new ValidationError(Messages.NullTypeType, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = typeRefExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            if (isWritten)
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeTypeReferenceExpression).ToString());
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
                error.UserData[RuleUserDataKeys.ErrorObject] = typeRefExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            Type resultType = validation.ResolveType(typeRefExpr.Type);
 
            return new RuleExpressionInfo(resultType);
        }
 
        internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
        {
            // These introduce no interesting dependencies or side-effects.
        }
 
        internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
        {
            // Type references don't evaluate to any value.
            return new RuleLiteralResult(null);
        }
 
        internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
        {
            CodeTypeReferenceExpression typeRefExpr = (CodeTypeReferenceExpression)expression;
            RuleDecompiler.DecompileType(stringBuilder, typeRefExpr.Type);
        }
 
        internal override CodeExpression Clone(CodeExpression expression)
        {
            CodeTypeReferenceExpression typeRefExpr = (CodeTypeReferenceExpression)expression;
            CodeTypeReferenceExpression newType = new CodeTypeReferenceExpression(CloneType(typeRefExpr.Type));
            return newType;
        }
 
        static internal CodeTypeReference CloneType(CodeTypeReference oldType)
        {
            if (oldType == null)
                return null;
 
            CodeTypeReference newType = new CodeTypeReference();
            newType.ArrayElementType = CloneType(oldType.ArrayElementType);
            newType.ArrayRank = oldType.ArrayRank;
            newType.BaseType = oldType.BaseType;
            foreach (CodeTypeReference typeReference in oldType.TypeArguments)
                newType.TypeArguments.Add(CloneType(typeReference));
 
            ConditionHelper.CloneUserData(oldType, newType);
 
            return newType;
        }
 
        internal override bool Match(CodeExpression expression, CodeExpression comperand)
        {
            CodeTypeReferenceExpression typeRefExpr = (CodeTypeReferenceExpression)expression;
            CodeTypeReferenceExpression newType = (CodeTypeReferenceExpression)comperand;
            return MatchType(typeRefExpr.Type, newType.Type);
        }
 
        static internal bool MatchType(CodeTypeReference typeRef1, CodeTypeReference typeRef2)
        {
            if (typeRef1.BaseType != typeRef2.BaseType)
                return false;
 
            if (typeRef1.TypeArguments.Count != typeRef2.TypeArguments.Count)
                return false;
            for (int i = 0; i < typeRef1.TypeArguments.Count; ++i)
            {
                CodeTypeReference trArg1 = typeRef1.TypeArguments[i];
                CodeTypeReference trArg2 = typeRef2.TypeArguments[i];
 
                if (!MatchType(trArg1, trArg2))
                    return false;
            }
 
            return true;
        }
    }
 
    #endregion
 
    #region Cast expression
 
    // CodeCastExpression
    internal class CastExpression : RuleExpressionInternal
    {
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
        {
            string message;
 
            CodeCastExpression castExpr = (CodeCastExpression)expression;
 
            if (isWritten)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeCastExpression).ToString());
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
                error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            if (castExpr.Expression == null)
            {
                ValidationError error = new ValidationError(Messages.NullCastExpr, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            if (castExpr.Expression is CodeTypeReferenceExpression)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, castExpr.Expression.GetType().FullName);
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
                error.UserData[RuleUserDataKeys.ErrorObject] = castExpr.Expression;
                validation.AddError(error);
                return null;
            }
 
            if (castExpr.TargetType == null)
            {
                ValidationError error = new ValidationError(Messages.NullCastType, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            // Figure out the operand type.
            RuleExpressionInfo operandInfo = RuleExpressionWalker.Validate(validation, castExpr.Expression, false);
            if (operandInfo == null)
                return null;
            Type fromType = operandInfo.ExpressionType;
 
            Type toType = validation.ResolveType(castExpr.TargetType);
            if (toType == null)
                return null;
 
            if (fromType == typeof(NullLiteral))
            {
                // Casting from null value.
                if (ConditionHelper.IsNonNullableValueType(toType))
                {
                    message = string.Format(CultureInfo.CurrentCulture, Messages.CastOfNullInvalid, RuleDecompiler.DecompileType(toType));
                    ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
                    error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
                    validation.Errors.Add(error);
                    return null;
                }
            }
            else
            {
                // Unwrap nullables to make life easy.
                Type fromType2 = fromType;
                if (ConditionHelper.IsNullableValueType(fromType2))
                    fromType2 = fromType2.GetGenericArguments()[0];
 
                Type toType2 = toType;
                if (ConditionHelper.IsNullableValueType(toType2))
                    toType2 = toType2.GetGenericArguments()[0];
 
                bool canConvert = false;
                if (fromType2.IsValueType && toType2.IsValueType)
                {
                    // Convert.ChangeType doesn't handle enum <--> numeric
                    // and float/double/decimal <--> char, which are allowed
                    if (fromType2.IsEnum)
                    {
                        canConvert = (toType2.IsEnum) || IsNumeric(toType2);
                    }
                    else if (toType2.IsEnum)
                    {
                        // don't need to check fromType for enum since it's handled above
                        canConvert = IsNumeric(fromType2);
                    }
                    else if (fromType2 == typeof(char))
                    {
                        canConvert = IsNumeric(toType2);
                    }
                    else if (toType2 == typeof(char))
                    {
                        canConvert = IsNumeric(fromType2);
                    }
                    else if (fromType2.IsPrimitive && toType2.IsPrimitive)
                    {
                        try
                        {
                            // note: this also allows bool <--> numeric conversions
                            object fromValueDefault = Activator.CreateInstance(fromType2);
                            Convert.ChangeType(fromValueDefault, toType2, CultureInfo.CurrentCulture);
                            canConvert = true;
                        }
                        catch (Exception)
                        {
                            canConvert = false;
                        }
                    }
                }
 
                if (!canConvert)
                {
                    // We can cast up or down an inheritence hierarchy,
                    // as well as support explicit and implicit overrides
                    ValidationError error;
                    canConvert = RuleValidation.ExplicitConversionSpecified(fromType, toType, out error);
                    if (error != null)
                    {
                        error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
                        validation.Errors.Add(error);
                        return null;
                    }
                }
 
                if (!canConvert)
                {
                    message = string.Format(CultureInfo.CurrentCulture, Messages.CastIncompatibleTypes, RuleDecompiler.DecompileType(fromType), RuleDecompiler.DecompileType(toType));
                    ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
                    error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
                    validation.Errors.Add(error);
                    return null;
                }
            }
 
            return new RuleExpressionInfo(toType);
        }
 
        private static bool IsNumeric(Type type)
        {
            switch (Type.GetTypeCode(type))
            {
                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;
                default:
                    return false;
            }
        }
 
        internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
        {
            // Just analyze the child.
            CodeCastExpression castExpr = (CodeCastExpression)expression;
            RuleExpressionWalker.AnalyzeUsage(analysis, castExpr.Expression, true, false, null);
        }
 
        internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
        {
            string message;
 
            CodeCastExpression castExpr = (CodeCastExpression)expression;
 
            // Evaluate the operand.
            object operandValue = RuleExpressionWalker.Evaluate(execution, castExpr.Expression).Value;
 
            // Get the cast-to type.
            RuleExpressionInfo castExprInfo = execution.Validation.ExpressionInfo(castExpr);
            if (castExprInfo == null)  // Oops, someone forgot to validate.
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
                InvalidOperationException exception = new InvalidOperationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = castExpr;
                throw exception;
            }
            Type toType = castExprInfo.ExpressionType;
 
            // Handle null operand result.
            if (operandValue == null)
            {
                // Here we are casting null to something. If it is a value type we can't do it.
                if (ConditionHelper.IsNonNullableValueType(toType))
                {
                    message = string.Format(CultureInfo.CurrentCulture, Messages.CastIncompatibleTypes, Messages.NullValue, RuleDecompiler.DecompileType(toType));
                    RuleEvaluationException exception = new RuleEvaluationException(message);
                    exception.Data[RuleUserDataKeys.ErrorObject] = castExpr;
                    throw exception;
                }
                // If it's not a value type, null is good.
            }
            else
            {
                Type operandType = execution.Validation.ExpressionInfo(castExpr.Expression).ExpressionType;
                operandValue = Executor.AdjustTypeWithCast(operandType, operandValue, toType);
            }
 
            return new RuleLiteralResult(operandValue);
        }
 
        internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
        {
            CodeCastExpression castExpr = (CodeCastExpression)expression;
 
            CodeExpression targetObject = castExpr.Expression;
            if (targetObject == null)
            {
                RuleEvaluationException exception = new RuleEvaluationException(Messages.NullCastExpr);
                exception.Data[RuleUserDataKeys.ErrorObject] = castExpr;
                throw exception;
            }
 
            if (castExpr.TargetType == null)
            {
                RuleEvaluationException exception = new RuleEvaluationException(Messages.NullCastType);
                exception.Data[RuleUserDataKeys.ErrorObject] = castExpr;
                throw exception;
            }
 
            bool mustParenthesize = RuleDecompiler.MustParenthesize(castExpr, parentExpression);
            if (mustParenthesize)
                stringBuilder.Append("(");
 
            stringBuilder.Append("(");
            RuleDecompiler.DecompileType(stringBuilder, castExpr.TargetType);
            stringBuilder.Append(")");
            RuleExpressionWalker.Decompile(stringBuilder, targetObject, castExpr);
 
            if (mustParenthesize)
                stringBuilder.Append(")");
        }
 
        internal override CodeExpression Clone(CodeExpression expression)
        {
            CodeCastExpression castExpr = (CodeCastExpression)expression;
            CodeCastExpression newCast = new CodeCastExpression();
            newCast.TargetType = TypeReferenceExpression.CloneType(castExpr.TargetType);
            newCast.Expression = RuleExpressionWalker.Clone(castExpr.Expression);
            return newCast;
        }
 
        internal override bool Match(CodeExpression expression, CodeExpression comperand)
        {
            CodeCastExpression castExpr = (CodeCastExpression)expression;
            CodeCastExpression castComperand = (CodeCastExpression)comperand;
 
            return TypeReferenceExpression.MatchType(castExpr.TargetType, castComperand.TargetType) &&
                RuleExpressionWalker.Match(castExpr.Expression, castComperand.Expression);
        }
    }
 
    #endregion
 
    #region Indexer Expression (indexer properties)
 
    // CodeIndexerExpression
    internal class IndexerPropertyExpression : RuleExpressionInternal
    {
        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
        {
            string message;
            ValidationError error = null;
            RulePropertyExpressionInfo propExprInfo = null;
            bool includeNonPublic = false;
            Type targetType = null;
 
            CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
 
            CodeExpression targetObject = indexerExpr.TargetObject;
            if (targetObject == null)
            {
                error = new ValidationError(Messages.NullIndexerTarget, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            if (targetObject is CodeTypeReferenceExpression)
            {
                error = new ValidationError(Messages.IndexersCannotBeStatic, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            if (indexerExpr.Indices == null || indexerExpr.Indices.Count == 0)
            {
                error = new ValidationError(Messages.MissingIndexExpressions, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            try
            {
                // Early exit from this if a cycle is detected.
                if (!validation.PushParentExpression(indexerExpr))
                    return null;
 
                RuleExpressionInfo targetExprInfo = RuleExpressionWalker.Validate(validation, indexerExpr.TargetObject, false);
                if (targetExprInfo == null)     // error occurred, so simply return
                    return null;
 
                targetType = targetExprInfo.ExpressionType;
                if (targetType == null)
                    return null;
 
                // if an error occurred (targetType == null), continue on to validate the arguments
                if (targetType == typeof(NullLiteral))
                {
                    message = string.Format(CultureInfo.CurrentCulture, Messages.NullIndexerTarget);
                    error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
                    error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
                    validation.Errors.Add(error);
                    targetType = null; // force exit after validating the arguments
                }
 
                List<CodeExpression> argExprs = new List<CodeExpression>();
 
                bool hasInvalidArgument = false;
                for (int i = 0; i < indexerExpr.Indices.Count; ++i)
                {
                    CodeExpression argExpr = indexerExpr.Indices[i];
                    if (argExpr == null)
                    {
                        error = new ValidationError(Messages.NullIndexExpression, ErrorNumbers.Error_ParameterNotSet);
                        error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
                        validation.Errors.Add(error);
                        hasInvalidArgument = true;
                    }
                    else
                    {
                        CodeDirectionExpression argDirection = argExpr as CodeDirectionExpression;
                        if (argDirection != null && argDirection.Direction != FieldDirection.In)
                        {
                            // No "ref" or "out" arguments are allowed on indexer arguments.
                            error = new ValidationError(Messages.IndexerArgCannotBeRefOrOut, ErrorNumbers.Error_IndexerArgCannotBeRefOrOut);
                            error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
                            validation.Errors.Add(error);
                            hasInvalidArgument = true;
                        }
 
                        if (argExpr is CodeTypeReferenceExpression)
                        {
                            message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, argExpr.GetType().FullName);
                            error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
                            error.UserData[RuleUserDataKeys.ErrorObject] = argExpr;
                            validation.AddError(error);
                            hasInvalidArgument = true;
                        }
 
                        // Validate the argument.
                        RuleExpressionInfo argExprInfo = RuleExpressionWalker.Validate(validation, argExpr, false);
                        if (argExprInfo == null)
                            hasInvalidArgument = true;
                        else
                            argExprs.Add(argExpr);
                    }
                }
 
                // Stop further validation if there was a problem with the target expression.
                if (targetType == null)
                    return null;
 
                // Stop further validation if there was a problem with any of the arguments.
                if (hasInvalidArgument)
                    return null;
 
                BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;
                if (validation.AllowInternalMembers(targetType))
                {
                    bindingFlags |= BindingFlags.NonPublic;
                    includeNonPublic = true;
                }
 
                // Everything okay so far, try to resolve the method.
                propExprInfo = validation.ResolveIndexerProperty(targetType, bindingFlags, argExprs, out error);
                if (propExprInfo == null)
                {
                    error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
                    validation.Errors.Add(error);
                    return null;
                }
            }
            finally
            {
                validation.PopParentExpression();
            }
 
            PropertyInfo pi = propExprInfo.PropertyInfo;
 
            MethodInfo accessorMethod = isWritten ? pi.GetSetMethod(includeNonPublic) : pi.GetGetMethod(includeNonPublic);
            if (accessorMethod == null)
            {
                string baseMessage = isWritten ? Messages.UnknownPropertySet : Messages.UnknownPropertyGet;
                message = string.Format(CultureInfo.CurrentCulture, baseMessage, pi.Name, RuleDecompiler.DecompileType(targetType));
                error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
                error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            if (!validation.ValidateMemberAccess(targetObject, targetType, accessorMethod, pi.Name, indexerExpr))
                return null;
 
            // Validate any RuleAttributes, if present.
            object[] attrs = pi.GetCustomAttributes(typeof(RuleAttribute), true);
            if (attrs != null && attrs.Length > 0)
            {
                Stack<MemberInfo> methodStack = new Stack<MemberInfo>();
                methodStack.Push(pi);
 
                bool allAttributesValid = true;
                foreach (RuleAttribute ruleAttr in attrs)
                {
                    if (!ruleAttr.Validate(validation, pi, targetType, pi.GetIndexParameters()))
                        allAttributesValid = false;
                }
 
                methodStack.Pop();
 
                if (!allAttributesValid)
                    return null;
            }
 
            return propExprInfo;
        }
 
 
        internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
        {
            string message;
 
            CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
 
            // Evaluate the target object and get its type.
            CodeExpression targetObject = indexerExpr.TargetObject;
            RuleExpressionInfo targetExprInfo = analysis.Validation.ExpressionInfo(targetObject);
            if (targetExprInfo == null)  // Oops, someone forgot to validate.
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
                InvalidOperationException exception = new InvalidOperationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = targetObject;
                throw exception;
            }
 
            // Get the property info from the validator so we can look for [RuleRead] and [RuleWrite] attributes.
            RulePropertyExpressionInfo propExprInfo = analysis.Validation.ExpressionInfo(indexerExpr) as RulePropertyExpressionInfo;
            if (propExprInfo == null)  // Oops, someone forgot to validate.
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
                InvalidOperationException exception = new InvalidOperationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = indexerExpr;
                throw exception;
            }
 
            PropertyInfo pi = propExprInfo.PropertyInfo;
 
            // Look for RuleAttribute's on the invoked indexer property.
            List<CodeExpression> attributedExprs = new List<CodeExpression>();
            analysis.AnalyzeRuleAttributes(pi, targetObject, qualifier, indexerExpr.Indices, pi.GetIndexParameters(), attributedExprs);
 
            // See if the target object needs default analysis.
            if (!attributedExprs.Contains(targetObject))
            {
                // The property had no [RuleRead] or [RuleWrite] attributes.  The target object is read or
                // written (as the case may be).
 
                RuleExpressionWalker.AnalyzeUsage(analysis, targetObject, isRead, isWritten, qualifier);
            }
 
            // See if any of the arguments need default analysis.
            for (int i = 0; i < indexerExpr.Indices.Count; ++i)
            {
                CodeExpression argExpr = indexerExpr.Indices[i];
 
                if (!attributedExprs.Contains(argExpr))
                {
                    // Similar to the target object, we assume that this method can reads the value
                    // of the parameter, but none of its members.
                    RuleExpressionWalker.AnalyzeUsage(analysis, argExpr, true, false, null);
                }
            }
        }
 
        internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
        {
            string message;
 
            CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
 
            RulePropertyExpressionInfo propExprInfo = execution.Validation.ExpressionInfo(indexerExpr) as RulePropertyExpressionInfo;
            if (propExprInfo == null)  // Oops, someone forgot to validate.
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
                InvalidOperationException exception = new InvalidOperationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = indexerExpr;
                throw exception;
            }
 
            PropertyInfo pi = propExprInfo.PropertyInfo;
 
            // Evaluate the target...
            object target = RuleExpressionWalker.Evaluate(execution, indexerExpr.TargetObject).Value;
 
            if (target == null)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.TargetEvaluatedNullIndexer);
                RuleEvaluationException exception = new RuleEvaluationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = indexerExpr;
                throw exception;
            }
 
            // Evaluate the index arguments.
            int actualArgCount = indexerExpr.Indices.Count;
            ParameterInfo[] parmInfos = pi.GetIndexParameters();
            object[] indexArgs = new object[parmInfos.Length];
 
            int numFixedParameters = parmInfos.Length;
            if (propExprInfo.NeedsParamsExpansion)
                numFixedParameters -= 1;
 
            int i;
            for (i = 0; i < numFixedParameters; ++i)
            {
                Type argType = execution.Validation.ExpressionInfo(indexerExpr.Indices[i]).ExpressionType;
                RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, indexerExpr.Indices[i]);
                indexArgs[i] = Executor.AdjustType(argType, argResult.Value, parmInfos[i].ParameterType);
            }
 
            if (numFixedParameters < actualArgCount)
            {
                // This target indexer had a params array, and we are calling it with an
                // expanded parameter list.  E.g.,
                //      int this[int x, params string[] y]
                // with the invocation:
                //      x.y[5, "crud", "kreeble", "glorp"]
                // We need to translate this to:
                //      x.y[5, new string[] { "crud", "kreeble", "glorp" }]
 
                ParameterInfo lastParamInfo = parmInfos[numFixedParameters];
 
                Type arrayType = lastParamInfo.ParameterType;
                System.Diagnostics.Debug.Assert(arrayType.IsArray);
                Type elementType = arrayType.GetElementType();
 
                Array paramsArray = (Array)arrayType.InvokeMember(arrayType.Name, BindingFlags.CreateInstance, null, null, new object[] { actualArgCount - i }, CultureInfo.CurrentCulture);
                for (; i < actualArgCount; ++i)
                {
                    Type argType = execution.Validation.ExpressionInfo(indexerExpr.Indices[i]).ExpressionType;
                    RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, indexerExpr.Indices[i]);
                    paramsArray.SetValue(Executor.AdjustType(argType, argResult.Value, elementType), i - numFixedParameters);
                }
 
                indexArgs[numFixedParameters] = paramsArray;
            }
 
            RulePropertyResult result = new RulePropertyResult(pi, target, indexArgs);
            return result;
        }
 
        internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
        {
            string message;
 
            CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
 
            CodeExpression targetObject = indexerExpr.TargetObject;
            if (targetObject == null)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.NullIndexerTarget);
                RuleEvaluationException exception = new RuleEvaluationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = indexerExpr;
                throw exception;
            }
 
            if (indexerExpr.Indices == null || indexerExpr.Indices.Count == 0)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.MissingIndexExpressions);
                RuleEvaluationException exception = new RuleEvaluationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = indexerExpr;
                throw exception;
            }
 
            RuleExpressionWalker.Decompile(stringBuilder, targetObject, indexerExpr);
            stringBuilder.Append('[');
            RuleExpressionWalker.Decompile(stringBuilder, indexerExpr.Indices[0], null);
            for (int i = 1; i < indexerExpr.Indices.Count; ++i)
            {
                stringBuilder.Append(", ");
                RuleExpressionWalker.Decompile(stringBuilder, indexerExpr.Indices[i], null);
            }
            stringBuilder.Append(']');
        }
 
        internal override CodeExpression Clone(CodeExpression expression)
        {
            CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
 
            CodeExpression targetObject = RuleExpressionWalker.Clone(indexerExpr.TargetObject);
 
            CodeExpression[] indices = new CodeExpression[indexerExpr.Indices.Count];
            for (int i = 0; i < indices.Length; ++i)
                indices[i] = RuleExpressionWalker.Clone(indexerExpr.Indices[i]);
 
            CodeIndexerExpression newIndexer = new CodeIndexerExpression(targetObject, indices);
            return newIndexer;
        }
 
        internal override bool Match(CodeExpression expression, CodeExpression comperand)
        {
            CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
 
            CodeIndexerExpression indexerComperand = (CodeIndexerExpression)comperand;
            if (!RuleExpressionWalker.Match(indexerExpr.TargetObject, indexerComperand.TargetObject))
                return false;
 
            if (indexerExpr.Indices.Count != indexerComperand.Indices.Count)
                return false;
 
            for (int i = 0; i < indexerExpr.Indices.Count; ++i)
            {
                if (!RuleExpressionWalker.Match(indexerExpr.Indices[i], indexerComperand.Indices[i]))
                    return false;
            }
 
            return true;
        }
    }
 
    #endregion
 
    #region Array Indexer Expression (indexer properties)
 
    // CodeArrayIndexerExpression
    internal class ArrayIndexerExpression : RuleExpressionInternal
    {
        internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
        {
            string message;
            ValidationError error = null;
            Type targetType = null;
 
            CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
 
            CodeExpression targetObject = arrayIndexerExpr.TargetObject;
            if (targetObject == null)
            {
                error = new ValidationError(Messages.NullIndexerTarget, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            if (targetObject is CodeTypeReferenceExpression)
            {
                error = new ValidationError(Messages.IndexersCannotBeStatic, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            if (arrayIndexerExpr.Indices == null || arrayIndexerExpr.Indices.Count == 0)
            {
                error = new ValidationError(Messages.MissingIndexExpressions, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
                validation.Errors.Add(error);
                return null;
            }
 
            try
            {
                // Early exit from this if a cycle is detected.
                if (!validation.PushParentExpression(arrayIndexerExpr))
                    return null;
 
                RuleExpressionInfo targetExprInfo = RuleExpressionWalker.Validate(validation, arrayIndexerExpr.TargetObject, false);
                if (targetExprInfo == null)     // error occurred, so simply return
                    return null;
 
                targetType = targetExprInfo.ExpressionType;
                if (targetType == null)
                    return null;
 
                // if an error occurred (targetType == null), continue on to validate the arguments
                if (targetType == typeof(NullLiteral))
                {
                    error = new ValidationError(Messages.NullIndexerTarget, ErrorNumbers.Error_ParameterNotSet);
                    error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
                    validation.Errors.Add(error);
                    return null;
                }
 
                // The target type better be an array.
                if (!targetType.IsArray)
                {
                    message = string.Format(CultureInfo.CurrentCulture, Messages.CannotIndexType, RuleDecompiler.DecompileType(targetType));
                    error = new ValidationError(message, ErrorNumbers.Error_CannotIndexType);
                    error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
                    validation.Errors.Add(error);
                    return null;
                }
 
                int rank = targetType.GetArrayRank();
                if (arrayIndexerExpr.Indices.Count != rank)
                {
                    message = string.Format(CultureInfo.CurrentCulture, Messages.ArrayIndexBadRank, rank);
                    error = new ValidationError(message, ErrorNumbers.Error_ArrayIndexBadRank);
                    error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
                    validation.Errors.Add(error);
                    return null;
                }
 
                bool hasInvalidArgument = false;
                for (int i = 0; i < arrayIndexerExpr.Indices.Count; ++i)
                {
                    CodeExpression argExpr = arrayIndexerExpr.Indices[i];
                    if (argExpr == null)
                    {
                        error = new ValidationError(Messages.NullIndexExpression, ErrorNumbers.Error_ParameterNotSet);
                        error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
                        validation.Errors.Add(error);
                        hasInvalidArgument = true;
                    }
                    else
                    {
                        CodeDirectionExpression argDirection = argExpr as CodeDirectionExpression;
                        if (argDirection != null)
                        {
                            // No "ref" or "out" arguments are allowed on indexer arguments.
                            error = new ValidationError(Messages.IndexerArgCannotBeRefOrOut, ErrorNumbers.Error_IndexerArgCannotBeRefOrOut);
                            error.UserData[RuleUserDataKeys.ErrorObject] = argExpr;
                            validation.Errors.Add(error);
                            hasInvalidArgument = true;
                        }
 
                        if (argExpr is CodeTypeReferenceExpression)
                        {
                            message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, argExpr.GetType().FullName);
                            error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
                            error.UserData[RuleUserDataKeys.ErrorObject] = argExpr;
                            validation.AddError(error);
                            hasInvalidArgument = true;
                        }
 
                        // Validate the argument.
                        RuleExpressionInfo argExprInfo = RuleExpressionWalker.Validate(validation, argExpr, false);
                        if (argExprInfo != null)
                        {
                            Type argType = argExprInfo.ExpressionType;
                            TypeCode argTypeCode = Type.GetTypeCode(argType);
 
                            // Any type that is, or can be converted to: int or long.
                            switch (argTypeCode)
                            {
                                case TypeCode.Byte:
                                case TypeCode.Char:
                                case TypeCode.Int16:
                                case TypeCode.Int32:
                                case TypeCode.Int64:
                                case TypeCode.SByte:
                                case TypeCode.UInt16:
                                    break;
 
                                default:
                                    message = string.Format(CultureInfo.CurrentCulture, Messages.ArrayIndexBadType, RuleDecompiler.DecompileType(argType));
                                    error = new ValidationError(message, ErrorNumbers.Error_ArrayIndexBadType);
                                    error.UserData[RuleUserDataKeys.ErrorObject] = argExpr;
                                    validation.Errors.Add(error);
                                    hasInvalidArgument = true;
                                    break;
                            }
                        }
                        else
                        {
                            hasInvalidArgument = true;
                        }
                    }
                }
 
                // Stop further validation if there was a problem with any of the arguments.
                if (hasInvalidArgument)
                    return null;
            }
            finally
            {
                validation.PopParentExpression();
            }
 
            // The result type is this array's element type.
            return new RuleExpressionInfo(targetType.GetElementType());
        }
 
 
        internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
        {
            // Analyze the target object, flowing down the qualifier from above.  An expression
            // like:
            //      this.a.b[2,3].c[4].d[5] = 99;
            // should produce a path similar to:
            //      this/a/b/c/d
 
            CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
            RuleExpressionWalker.AnalyzeUsage(analysis, arrayIndexerExpr.TargetObject, isRead, isWritten, qualifier);
 
            // Analyze the indexer arguments.  They are read.
            for (int i = 0; i < arrayIndexerExpr.Indices.Count; ++i)
                RuleExpressionWalker.AnalyzeUsage(analysis, arrayIndexerExpr.Indices[i], true, false, null);
        }
 
        internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
        {
            CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
 
            // Evaluate the target...
            object target = RuleExpressionWalker.Evaluate(execution, arrayIndexerExpr.TargetObject).Value;
 
            if (target == null)
            {
                string message = string.Format(CultureInfo.CurrentCulture, Messages.TargetEvaluatedNullIndexer);
                RuleEvaluationException exception = new RuleEvaluationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
                throw exception;
            }
 
            // Evaluate the index arguments (converting them to "longs")
            int actualArgCount = arrayIndexerExpr.Indices.Count;
            long[] indexArgs = new long[actualArgCount];
 
            for (int i = 0; i < actualArgCount; ++i)
            {
                Type argType = execution.Validation.ExpressionInfo(arrayIndexerExpr.Indices[i]).ExpressionType;
                object argValue = RuleExpressionWalker.Evaluate(execution, arrayIndexerExpr.Indices[i]).Value;
                indexArgs[i] = (long)Executor.AdjustType(argType, argValue, typeof(long));
            }
 
            RuleArrayElementResult result = new RuleArrayElementResult((Array)target, indexArgs);
            return result;
        }
 
        internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
        {
            string message;
 
            CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
 
            CodeExpression targetObject = arrayIndexerExpr.TargetObject;
            if (targetObject == null)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.NullIndexerTarget);
                RuleEvaluationException exception = new RuleEvaluationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
                throw exception;
            }
 
            if (arrayIndexerExpr.Indices == null || arrayIndexerExpr.Indices.Count == 0)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.MissingIndexExpressions);
                RuleEvaluationException exception = new RuleEvaluationException(message);
                exception.Data[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
                throw exception;
            }
 
            RuleExpressionWalker.Decompile(stringBuilder, targetObject, arrayIndexerExpr);
            stringBuilder.Append('[');
            RuleExpressionWalker.Decompile(stringBuilder, arrayIndexerExpr.Indices[0], null);
            for (int i = 1; i < arrayIndexerExpr.Indices.Count; ++i)
            {
                stringBuilder.Append(", ");
                RuleExpressionWalker.Decompile(stringBuilder, arrayIndexerExpr.Indices[i], null);
            }
            stringBuilder.Append(']');
        }
 
        internal override CodeExpression Clone(CodeExpression expression)
        {
            CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
 
            CodeExpression targetObject = RuleExpressionWalker.Clone(arrayIndexerExpr.TargetObject);
 
            CodeExpression[] indices = new CodeExpression[arrayIndexerExpr.Indices.Count];
            for (int i = 0; i < indices.Length; ++i)
                indices[i] = RuleExpressionWalker.Clone(arrayIndexerExpr.Indices[i]);
 
            CodeArrayIndexerExpression newIndexer = new CodeArrayIndexerExpression(targetObject, indices);
            return newIndexer;
        }
 
        internal override bool Match(CodeExpression expression, CodeExpression comperand)
        {
            CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
 
            CodeArrayIndexerExpression indexerComperand = (CodeArrayIndexerExpression)comperand;
            if (!RuleExpressionWalker.Match(arrayIndexerExpr.TargetObject, indexerComperand.TargetObject))
                return false;
 
            if (arrayIndexerExpr.Indices.Count != indexerComperand.Indices.Count)
                return false;
 
            for (int i = 0; i < arrayIndexerExpr.Indices.Count; ++i)
            {
                if (!RuleExpressionWalker.Match(arrayIndexerExpr.Indices[i], indexerComperand.Indices[i]))
                    return false;
            }
 
            return true;
        }
    }
 
    #endregion
 
    #region Object Create expression
    internal class ObjectCreateExpression : RuleExpressionInternal
    {
        internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
        {
            string message;
            ValidationError error;
 
            CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
 
            if (isWritten)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeObjectCreateExpression).ToString());
                error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
                error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
                validation.Errors.Add(error);
                return null;
            }
 
            if (createExpression.CreateType == null)
            {
                error = new ValidationError(Messages.NullTypeType, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
                validation.Errors.Add(error);
                return null;
            }
 
            Type resultType = validation.ResolveType(createExpression.CreateType);
            if (resultType == null)
                return null;
 
            // look up parameters
            List<CodeExpression> parameters = new List<CodeExpression>();
            try
            {
                // Early exit from this if a cycle is detected.
                if (!validation.PushParentExpression(createExpression))
                    return null;
 
                bool hasInvalidArgument = false;
                for (int i = 0; i < createExpression.Parameters.Count; ++i)
                {
                    CodeExpression parameter = createExpression.Parameters[i];
                    if (parameter == null)
                    {
                        message = string.Format(CultureInfo.CurrentCulture, Messages.NullConstructorParameter, i.ToString(CultureInfo.CurrentCulture), RuleDecompiler.DecompileType(resultType));
                        error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
                        error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
                        validation.Errors.Add(error);
                        hasInvalidArgument = true;
                    }
                    else
                    {
                        RuleExpressionInfo parameterInfo = RuleExpressionWalker.Validate(validation, parameter, false);
                        if (parameterInfo == null)
                            hasInvalidArgument = true;
                        parameters.Add(parameter);
                    }
                }
                // quit if parameters not valid
                if (hasInvalidArgument)
                    return null;
            }
            finally
            {
                validation.PopParentExpression();
            }
 
            // see if we can find the matching constructor
            BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;
            if (validation.AllowInternalMembers(resultType))
                bindingFlags |= BindingFlags.NonPublic;
 
            // creating a value-type object with no parameters can always be done
            if ((resultType.IsValueType) && (parameters.Count == 0))
                return new RuleExpressionInfo(resultType);
 
            // error if type is an abstract type
            if (resultType.IsAbstract)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownConstructor, RuleDecompiler.DecompileType(resultType));
                error = new ValidationError(message, ErrorNumbers.Error_MethodNotExists);
                error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
                validation.Errors.Add(error);
                return null;
            }
 
            RuleConstructorExpressionInfo constructorInvokeInfo = validation.ResolveConstructor(resultType, bindingFlags, parameters, out error);
            if (constructorInvokeInfo == null)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownConstructor, RuleDecompiler.DecompileType(resultType));
                error = new ValidationError(message, ErrorNumbers.Error_MethodNotExists);
                error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
                validation.Errors.Add(error);
                return null;
            }
 
            return constructorInvokeInfo;
        }
 
        internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
        {
            CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
 
            // check each parameter
            foreach (CodeExpression p in createExpression.Parameters)
            {
                RuleExpressionWalker.AnalyzeUsage(analysis, p, true, false, null);
            }
        }
 
        internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
        {
            CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
 
            if (createExpression.CreateType == null)
            {
                RuleEvaluationException exception = new RuleEvaluationException(Messages.NullTypeType);
                exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
                throw exception;
            }
 
            RuleExpressionInfo expressionInfo = execution.Validation.ExpressionInfo(createExpression);
            if (expressionInfo == null)  // Oops, someone forgot to validate.
            {
                InvalidOperationException exception = new InvalidOperationException(Messages.ExpressionNotValidated);
                exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
                throw exception;
            }
 
            RuleConstructorExpressionInfo createExpressionInfo = expressionInfo as RuleConstructorExpressionInfo;
            if (createExpressionInfo == null)
            {
                // it's just a regular RuleExpressionInfo, which means this is a value-type with no parameters
                return new RuleLiteralResult(Activator.CreateInstance(expressionInfo.ExpressionType));
            }
 
            ConstructorInfo constructor = createExpressionInfo.ConstructorInfo;
            object[] arguments = null;
            RuleExpressionResult[] outArgumentResults = null;
 
            if (createExpression.Parameters != null && createExpression.Parameters.Count > 0)
            {
                int actualArgCount = createExpression.Parameters.Count;
                ParameterInfo[] parmInfos = constructor.GetParameters();
 
                arguments = new object[parmInfos.Length];
 
                int numFixedParameters = parmInfos.Length;
                if (createExpressionInfo.NeedsParamsExpansion)
                    numFixedParameters -= 1;
 
                int i;
 
                // Evaluate the fixed portion of the parameter list.
                for (i = 0; i < numFixedParameters; ++i)
                {
                    Type argType = execution.Validation.ExpressionInfo(createExpression.Parameters[i]).ExpressionType;
                    RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, createExpression.Parameters[i]);
 
                    // Special procesing of direction expressions to keep track of out arguments (& ref).
                    CodeDirectionExpression direction = createExpression.Parameters[i] as CodeDirectionExpression;
                    if (direction != null && (direction.Direction == FieldDirection.Ref || direction.Direction == FieldDirection.Out))
                    {
                        // lazy creation of fieldsToSet
                        if (outArgumentResults == null)
                            outArgumentResults = new RuleExpressionResult[actualArgCount];
                        // keep track of this out expression so we can set it later
                        outArgumentResults[i] = argResult;
                    }
 
                    arguments[i] = Executor.AdjustType(argType, argResult.Value, parmInfos[i].ParameterType);
                }
 
                if (numFixedParameters < actualArgCount)
                {
                    // This target method had a params array, and we are calling it with an
                    // expanded parameter list.  E.g.,
                    //      void foo(int x, params string[] y)
                    // with the invocation:
                    //      foo(5, "crud", "kreeble", "glorp")
                    // We need to translate this to:
                    //      foo(5, new string[] { "crud", "kreeble", "glorp" })
 
                    ParameterInfo lastParamInfo = parmInfos[numFixedParameters];
 
                    Type arrayType = lastParamInfo.ParameterType;
                    System.Diagnostics.Debug.Assert(arrayType.IsArray);
                    Type elementType = arrayType.GetElementType();
 
                    Array paramsArray = Array.CreateInstance(elementType, actualArgCount - i);
                    for (; i < actualArgCount; ++i)
                    {
                        Type argType = execution.Validation.ExpressionInfo(createExpression.Parameters[i]).ExpressionType;
                        RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, createExpression.Parameters[i]);
                        paramsArray.SetValue(Executor.AdjustType(argType, argResult.Value, elementType), i - numFixedParameters);
                    }
 
                    arguments[numFixedParameters] = paramsArray;
                }
            }
 
            object result;
            try
            {
                result = constructor.Invoke(arguments);
            }
            catch (TargetInvocationException e)
            {
                // if there is no inner exception, leave it untouched
                if (e.InnerException == null)
                    throw;
                string message = string.Format(CultureInfo.CurrentCulture,
                    Messages.Error_ConstructorInvoke,
                    RuleDecompiler.DecompileType(createExpressionInfo.ExpressionType),
                    e.InnerException.Message);
                throw new TargetInvocationException(message, e.InnerException);
            }
 
            // any out/ref parameters that need to be assigned?
            if (outArgumentResults != null)
            {
                for (int i = 0; i < createExpression.Parameters.Count; ++i)
                {
                    if (outArgumentResults[i] != null)
                        outArgumentResults[i].Value = arguments[i];
                }
            }
            return new RuleLiteralResult(result);
        }
 
        internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
        {
            CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
 
            bool mustParenthesize = RuleDecompiler.MustParenthesize(createExpression, parentExpression);
            if (mustParenthesize)
                stringBuilder.Append("(");
 
            stringBuilder.Append("new ");
            RuleDecompiler.DecompileType(stringBuilder, createExpression.CreateType);
 
            // Decompile the arguments
            stringBuilder.Append('(');
            for (int i = 0; i < createExpression.Parameters.Count; ++i)
            {
                CodeExpression paramExpr = createExpression.Parameters[i];
                if (paramExpr == null)
                {
                    string message = string.Format(CultureInfo.CurrentCulture, Messages.NullConstructorTypeParameter, i.ToString(CultureInfo.CurrentCulture), createExpression.CreateType);
                    RuleEvaluationException exception = new RuleEvaluationException(message);
                    exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
                    throw exception;
                }
 
                if (i > 0)
                    stringBuilder.Append(", ");
 
                RuleExpressionWalker.Decompile(stringBuilder, paramExpr, null);
            }
            stringBuilder.Append(')');
 
            if (mustParenthesize)
                stringBuilder.Append(")");
        }
 
        internal override CodeExpression Clone(CodeExpression expression)
        {
            CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
 
            CodeObjectCreateExpression newCreate = new CodeObjectCreateExpression();
            newCreate.CreateType = TypeReferenceExpression.CloneType(createExpression.CreateType);
            foreach (CodeExpression p in createExpression.Parameters)
            {
                newCreate.Parameters.Add(RuleExpressionWalker.Clone(p));
            }
            return newCreate;
        }
 
        internal override bool Match(CodeExpression expression, CodeExpression comperand)
        {
            CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
 
            CodeObjectCreateExpression createComperand = comperand as CodeObjectCreateExpression;
            if (createComperand == null)
                return false;
            // check types
            if (!TypeReferenceExpression.MatchType(createExpression.CreateType, createComperand.CreateType))
                return false;
            // check parameters
            if (createExpression.Parameters.Count != createComperand.Parameters.Count)
                return false;
            for (int i = 0; i < createExpression.Parameters.Count; ++i)
                if (!RuleExpressionWalker.Match(createExpression.Parameters[i], createComperand.Parameters[i]))
                    return false;
            return true;
        }
    }
    #endregion
 
    #region Array Create expression
    internal class ArrayCreateExpression : RuleExpressionInternal
    {
        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
        {
            string message;
 
            CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
 
            if (isWritten)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeObjectCreateExpression).ToString());
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
                error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
                validation.Errors.Add(error);
                return null;
            }
 
            if (createExpression.CreateType == null)
            {
                ValidationError error = new ValidationError(Messages.NullTypeType, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
                validation.Errors.Add(error);
                return null;
            }
 
            Type resultType = validation.ResolveType(createExpression.CreateType);
            if (resultType == null)
                return null;
 
            // size CodeDom has limited support for arrays (only 1 dimensional, not more)
            // we limit CodeArrayCreateExpression to a single dimension
            // (i.e. CodeArrayCreateExpression cannot define int[5,3] or int[5][3], 
            // but it is possible to define int[][3] and then use initializers to 
            // fill it in. But we only support int[] or int[3].)
            if (resultType.IsArray)
            {
                message = string.Format(CultureInfo.CurrentCulture, Messages.ArrayTypeInvalid, resultType.Name);
                ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
                error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
                validation.Errors.Add(error);
                return null;
            }
 
            try
            {
                // Early exit from this if a cycle is detected.
                if (!validation.PushParentExpression(createExpression))
                    return null;
 
                if (createExpression.Size < 0)
                {
                    ValidationError error = new ValidationError(Messages.ArraySizeInvalid, ErrorNumbers.Error_ParameterNotSet);
                    error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
                    validation.Errors.Add(error);
                    return null;
                }
 
                // look up size (if specified)
                if (createExpression.SizeExpression != null)
                {
                    RuleExpressionInfo sizeInfo = RuleExpressionWalker.Validate(validation, createExpression.SizeExpression, false);
                    if (sizeInfo == null)
                        return null;
                    if ((sizeInfo.ExpressionType != typeof(int))
                        && (sizeInfo.ExpressionType != typeof(uint))
                        && (sizeInfo.ExpressionType != typeof(long))
                        && (sizeInfo.ExpressionType != typeof(ulong)))
                    {
                        message = string.Format(CultureInfo.CurrentCulture, Messages.ArraySizeTypeInvalid, sizeInfo.ExpressionType.Name);
                        ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
                        error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
                        validation.Errors.Add(error);
                        return null;
                    }
                }
                bool parameterInvalid = false;
                for (int i = 0; i < createExpression.Initializers.Count; ++i)
                {
                    CodeExpression init = createExpression.Initializers[i];
                    if (init == null)
                    {
                        message = string.Format(CultureInfo.CurrentCulture, Messages.MissingInitializer, resultType.Name);
                        ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
                        error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
                        validation.Errors.Add(error);
                        return null;
                    }
                    RuleExpressionInfo parameterInfo = RuleExpressionWalker.Validate(validation, init, false);
                    if (parameterInfo == null)
                    {
                        parameterInvalid = true;
                    }
                    else
                    {
                        // can we convert the result type to the array type?
                        ValidationError error;
                        if (!RuleValidation.StandardImplicitConversion(parameterInfo.ExpressionType, resultType, init, out error))
                        {
                            // types must match
                            if (error != null)
                            {
                                // we got an error from the conversion, so give it back as well as a new error
                                error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
                                validation.Errors.Add(error);
                            }
                            message = string.Format(CultureInfo.CurrentCulture, Messages.InitializerMismatch, i, resultType.Name);
                            error = new ValidationError(message, ErrorNumbers.Error_OperandTypesIncompatible);
                            error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
                            validation.Errors.Add(error);
                            return null;
                        }
                    }
                }
                // if any errors get out
                if (parameterInvalid)
                    return null;
 
                // now it gets tricky. CodeArrayCreateExpression constructors allow:
                //     1) size as int
                //     2) size as CodeExpression
                //     3) initializers as params array
                // However, we allow a size and initializers, so try to verify size >= #initializers
                // size can be an int, uint, long, or ulong
                double size = -1;
                if (createExpression.SizeExpression != null)
                {
                    CodePrimitiveExpression prim = createExpression.SizeExpression as CodePrimitiveExpression;
                    if ((prim != null) && (prim.Value != null))
                        size = (double)Executor.AdjustType(prim.Value.GetType(), prim.Value, typeof(double));
                    if (createExpression.Size > 0)
                    {
                        // both size and SizeExpression specified, complain
                        ValidationError error = new ValidationError(Messages.ArraySizeBoth, ErrorNumbers.Error_ParameterNotSet);
                        error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
                        validation.Errors.Add(error);
                        return null;
                    }
                }
                else if (createExpression.Size > 0)
                    size = createExpression.Size;
 
                if ((size >= 0) && (createExpression.Initializers.Count > size))
                {
                    message = string.Format(CultureInfo.CurrentCulture, Messages.InitializerCountMismatch, createExpression.Initializers.Count, size);
                    ValidationError error = new ValidationError(message, ErrorNumbers.Error_OperandTypesIncompatible);
                    error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
                    validation.Errors.Add(error);
                    return null;
                }
            }
            finally
            {
                validation.PopParentExpression();
            }
            return new RuleExpressionInfo(resultType.MakeArrayType());
        }
 
        internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
        {
            CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
 
            if (createExpression.SizeExpression != null)
                RuleExpressionWalker.AnalyzeUsage(analysis, createExpression.SizeExpression, true, false, null);
            foreach (CodeExpression p in createExpression.Initializers)
                RuleExpressionWalker.AnalyzeUsage(analysis, p, true, false, null);
        }
 
        internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
        {
            CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
 
            if (createExpression.CreateType == null)
            {
                RuleEvaluationException exception = new RuleEvaluationException(Messages.NullTypeType);
                exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
                throw exception;
            }
 
            RuleExpressionInfo createExpressionInfo = execution.Validation.ExpressionInfo(createExpression);
            if (createExpression == null)  // Oops, someone forgot to validate.
            {
                InvalidOperationException exception = new InvalidOperationException(Messages.ExpressionNotValidated);
                exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
                throw exception;
            }
 
            // type should be an array type already
            Type type = createExpressionInfo.ExpressionType;
            Type elementType = type.GetElementType();
 
            // assume this has been validated, so only 1 size specified
            int size = 0;
            if (createExpression.SizeExpression != null)
            {
                Type sizeType = execution.Validation.ExpressionInfo(createExpression.SizeExpression).ExpressionType;
                RuleExpressionResult sizeResult = RuleExpressionWalker.Evaluate(execution, createExpression.SizeExpression);
                if (sizeType == typeof(int))
                    size = (int)sizeResult.Value;
                else if (sizeType == typeof(long))
                    size = (int)((long)sizeResult.Value);
                else if (sizeType == typeof(uint))
                    size = (int)((uint)sizeResult.Value);
                else if (sizeType == typeof(ulong))
                    size = (int)((ulong)sizeResult.Value);
            }
            else if (createExpression.Size != 0)
                size = createExpression.Size;
            else
                size = createExpression.Initializers.Count;
 
            Array result = Array.CreateInstance(elementType, size);
            if (createExpression.Initializers != null)
                for (int i = 0; i < createExpression.Initializers.Count; ++i)
                {
                    CodeExpression initializer = createExpression.Initializers[i];
                    Type initializerType = execution.Validation.ExpressionInfo(initializer).ExpressionType;
                    RuleExpressionResult initializerResult = RuleExpressionWalker.Evaluate(execution, initializer);
                    result.SetValue(Executor.AdjustType(initializerType, initializerResult.Value, elementType), i);
                }
            return new RuleLiteralResult(result);
        }
 
        internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
        {
            CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
 
            bool mustParenthesize = RuleDecompiler.MustParenthesize(createExpression, parentExpression);
            if (mustParenthesize)
                stringBuilder.Append("(");
 
            stringBuilder.Append("new ");
            RuleDecompiler.DecompileType(stringBuilder, createExpression.CreateType);
            stringBuilder.Append('[');
            if (createExpression.SizeExpression != null)
                RuleExpressionWalker.Decompile(stringBuilder, createExpression.SizeExpression, null);
            else if ((createExpression.Size != 0) || (createExpression.Initializers.Count == 0))
                stringBuilder.Append(createExpression.Size);
            stringBuilder.Append(']');
            if (createExpression.Initializers.Count > 0)
            {
                stringBuilder.Append(" { ");
                for (int i = 0; i < createExpression.Initializers.Count; ++i)
                {
                    CodeExpression paramExpr = createExpression.Initializers[i];
                    if (paramExpr == null)
                    {
                        string message = string.Format(CultureInfo.CurrentCulture, Messages.NullConstructorTypeParameter, i.ToString(CultureInfo.CurrentCulture), createExpression.CreateType);
                        RuleEvaluationException exception = new RuleEvaluationException(message);
                        exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
                        throw exception;
                    }
 
                    if (i > 0)
                        stringBuilder.Append(", ");
 
                    RuleExpressionWalker.Decompile(stringBuilder, paramExpr, null);
                }
                stringBuilder.Append('}');
            }
 
            if (mustParenthesize)
                stringBuilder.Append(")");
        }
 
        internal override CodeExpression Clone(CodeExpression expression)
        {
            CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
 
            CodeArrayCreateExpression newCreate = new CodeArrayCreateExpression();
            newCreate.CreateType = TypeReferenceExpression.CloneType(createExpression.CreateType);
            newCreate.Size = createExpression.Size;
            if (createExpression.SizeExpression != null)
                newCreate.SizeExpression = RuleExpressionWalker.Clone(createExpression.SizeExpression);
            foreach (CodeExpression p in createExpression.Initializers)
                newCreate.Initializers.Add(RuleExpressionWalker.Clone(p));
            return newCreate;
        }
 
        internal override bool Match(CodeExpression expression, CodeExpression comperand)
        {
            CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
 
            CodeArrayCreateExpression createComperand = comperand as CodeArrayCreateExpression;
            if ((createComperand == null)
                || (createExpression.Size != createComperand.Size)
                || (!TypeReferenceExpression.MatchType(createExpression.CreateType, createComperand.CreateType)))
                return false;
            // check SizeExpression, if it exists
            if (createExpression.SizeExpression != null)
            {
                if (createComperand.SizeExpression == null)
                    return false;
                if (!RuleExpressionWalker.Match(createExpression.SizeExpression, createComperand.SizeExpression))
                    return false;
            }
            else
            {
                if (createComperand.SizeExpression != null)
                    return false;
            }
            // check initializers
            if (createExpression.Initializers.Count != createComperand.Initializers.Count)
                return false;
            for (int i = 0; i < createExpression.Initializers.Count; ++i)
                if (!RuleExpressionWalker.Match(createExpression.Initializers[i], createComperand.Initializers[i]))
                    return false;
            return true;
        }
    }
    #endregion
}