1 // ---------------------------------------------------------------------------
2 // Copyright (C) 2006 Microsoft Corporation All Rights Reserved
3 // ---------------------------------------------------------------------------
4 
5 #define CODE_ANALYSIS
6 using System.CodeDom;
7 using System.Collections.Generic;
8 using System.Diagnostics.CodeAnalysis;
9 using System.Globalization;
10 using System.Reflection;
11 using System.Text;
12 using System.Workflow.ComponentModel;
13 using System.Workflow.ComponentModel.Compiler;
14 using System.Workflow.Activities.Common;
15 
16 namespace System.Workflow.Activities.Rules
17 {
18     public interface IRuleExpression
19     {
Validate(RuleValidation validation, bool isWritten)20         RuleExpressionInfo Validate(RuleValidation validation, bool isWritten);
Evaluate(RuleExecution execution)21         RuleExpressionResult Evaluate(RuleExecution execution);
AnalyzeUsage(RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)22         void AnalyzeUsage(RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier);
23         [SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters", MessageId = "0#")]
Decompile(StringBuilder stringBuilder, CodeExpression parentExpression)24         void Decompile(StringBuilder stringBuilder, CodeExpression parentExpression);
Match(CodeExpression expression)25         bool Match(CodeExpression expression);
Clone()26         CodeExpression Clone();
27     }
28 
29     internal abstract class RuleExpressionInternal
30     {
Validate(CodeExpression expression, RuleValidation validation, bool isWritten)31         internal abstract RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten);
Evaluate(CodeExpression expression, RuleExecution execution)32         internal abstract RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution);
AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)33         internal abstract void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier);
Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)34         internal abstract void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression);
Match(CodeExpression leftExpression, CodeExpression rightExpression)35         internal abstract bool Match(CodeExpression leftExpression, CodeExpression rightExpression);
Clone(CodeExpression expression)36         internal abstract CodeExpression Clone(CodeExpression expression);
37     }
38 
39 
40     #region "this" expression
41 
42     // CodeThisReferenceExpression
43     internal class ThisExpression : RuleExpressionInternal
44     {
Validate(CodeExpression expression, RuleValidation validation, bool isWritten)45         internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
46         {
47             if (isWritten)
48             {
49                 string message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeThisReferenceExpression).ToString());
50                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
51                 error.UserData[RuleUserDataKeys.ErrorObject] = expression;
52                 validation.Errors.Add(error);
53                 return null;
54             }
55 
56             return new RuleExpressionInfo(validation.ThisType);
57         }
58 
AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)59         internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
60         {
61             if (analysis.ForWrites && !isWritten)            // If we're tracking writes, then ignore things that aren't written.
62                 return;
63             else if (!analysis.ForWrites && !isRead)   // ... and vice-versa
64                 return;
65 
66             StringBuilder sb = new StringBuilder("this/");
67             for (RulePathQualifier q = qualifier; q != null; q = q.Next)
68             {
69                 sb.Append(q.Name);
70                 if (q.Name == "*")
71                 {
72                     if (q.Next != null)
73                         throw new NotSupportedException(Messages.InvalidWildCardInPathQualifier);
74                 }
75                 else
76                 {
77                     sb.Append("/");
78                 }
79             }
80 
81             // Add the symbol to our set.
82             analysis.AddSymbol(sb.ToString());
83         }
84 
Evaluate(CodeExpression expression, RuleExecution execution)85         internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
86         {
87             return execution.ThisLiteralResult;
88         }
89 
Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)90         internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
91         {
92             stringBuilder.Append("this");
93         }
94 
Clone(CodeExpression expression)95         internal override CodeExpression Clone(CodeExpression expression)
96         {
97             return new CodeThisReferenceExpression();
98         }
99 
Match(CodeExpression expression, CodeExpression comperand)100         internal override bool Match(CodeExpression expression, CodeExpression comperand)
101         {
102             // We already verified their types match.
103             return true;
104         }
105     }
106 
107     #endregion
108 
109     #region Primitive expression
110 
111     // CodePrimitiveExpression
112     internal class PrimitiveExpression : RuleExpressionInternal
113     {
Validate(CodeExpression expression, RuleValidation validation, bool isWritten)114         internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
115         {
116             if (isWritten)
117             {
118                 string message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodePrimitiveExpression).ToString());
119                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
120                 error.UserData[RuleUserDataKeys.ErrorObject] = expression;
121                 validation.Errors.Add(error);
122                 return null;
123             }
124 
125             CodePrimitiveExpression primitiveExpr = (CodePrimitiveExpression)expression;
126             Type resultType = (primitiveExpr.Value != null) ? primitiveExpr.Value.GetType() : typeof(NullLiteral);
127             return new RuleExpressionInfo(resultType);
128         }
129 
AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)130         internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
131         {
132             // Literal values have no interesting dependencies or side-effects.
133         }
134 
Evaluate(CodeExpression expression, RuleExecution execution)135         internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
136         {
137             CodePrimitiveExpression primitiveExpr = (CodePrimitiveExpression)expression;
138             return new RuleLiteralResult(primitiveExpr.Value);
139         }
140 
Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)141         internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
142         {
143             CodePrimitiveExpression primitiveExpr = (CodePrimitiveExpression)expression;
144             RuleDecompiler.DecompileObjectLiteral(stringBuilder, primitiveExpr.Value);
145         }
146 
Clone(CodeExpression expression)147         internal override CodeExpression Clone(CodeExpression expression)
148         {
149             CodePrimitiveExpression primitiveExpr = (CodePrimitiveExpression)expression;
150             object clonedValue = ConditionHelper.CloneObject(primitiveExpr.Value);
151             return new CodePrimitiveExpression(clonedValue);
152         }
153 
Match(CodeExpression expression, CodeExpression comperand)154         internal override bool Match(CodeExpression expression, CodeExpression comperand)
155         {
156             CodePrimitiveExpression primitiveExpr = (CodePrimitiveExpression)expression;
157             CodePrimitiveExpression comperandPrimitive = (CodePrimitiveExpression)comperand;
158 
159             if (primitiveExpr.Value == comperandPrimitive.Value)
160                 return true;
161 
162             if (primitiveExpr.Value == null || comperandPrimitive.Value == null)
163                 return false;
164 
165             return primitiveExpr.Value.Equals(comperandPrimitive.Value);
166         }
167     }
168 
169     #endregion
170 
171     #region Binary expression
172 
173     // CodeBinaryOperatorExpression
174     internal class BinaryExpression : RuleExpressionInternal
175     {
176         #region Validate
177 
178         [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
Validate(CodeExpression expression, RuleValidation validation, bool isWritten)179         internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
180         {
181             string message;
182             ValidationError error;
183 
184             CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
185 
186             // Early exit from this if a cycle is detected.
187             if (!validation.PushParentExpression(binaryExpr))
188                 return null;
189 
190             if (isWritten)
191             {
192                 message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeBinaryOperatorExpression).ToString());
193                 error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
194                 error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
195                 validation.Errors.Add(error);
196             }
197 
198             RuleExpressionInfo lhsExprInfo = null;
199             RuleExpressionInfo rhsExprInfo = null;
200 
201             if (binaryExpr.Left == null)
202             {
203                 message = string.Format(CultureInfo.CurrentCulture, Messages.NullBinaryOpLHS, binaryExpr.Operator.ToString());
204                 error = new ValidationError(message, ErrorNumbers.Error_LeftOperandMissing);
205                 error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
206                 validation.Errors.Add(error);
207             }
208             else
209             {
210                 if (binaryExpr.Left is CodeTypeReferenceExpression)
211                 {
212                     message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, binaryExpr.Left.GetType().FullName);
213                     error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
214                     error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr.Left;
215                     validation.AddError(error);
216                     return null;
217                 }
218 
219                 lhsExprInfo = RuleExpressionWalker.Validate(validation, binaryExpr.Left, false);
220             }
221 
222             if (binaryExpr.Right == null)
223             {
224                 message = string.Format(CultureInfo.CurrentCulture, Messages.NullBinaryOpRHS, binaryExpr.Operator.ToString());
225                 error = new ValidationError(message, ErrorNumbers.Error_RightOperandMissing);
226                 error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
227                 validation.Errors.Add(error);
228             }
229             else
230             {
231                 if (binaryExpr.Right is CodeTypeReferenceExpression)
232                 {
233                     message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, binaryExpr.Right.GetType().FullName);
234                     error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
235                     error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr.Right;
236                     validation.AddError(error);
237                     return null;
238                 }
239 
240                 rhsExprInfo = RuleExpressionWalker.Validate(validation, binaryExpr.Right, false);
241             }
242 
243             validation.PopParentExpression();
244 
245             RuleBinaryExpressionInfo resultExprInfo = null;
246 
247             if (lhsExprInfo != null && rhsExprInfo != null)
248             {
249                 Type lhsType = lhsExprInfo.ExpressionType;
250                 Type rhsType = rhsExprInfo.ExpressionType;
251 
252                 switch (binaryExpr.Operator)
253                 {
254                     case CodeBinaryOperatorType.Add:
255                     case CodeBinaryOperatorType.Subtract:
256                     case CodeBinaryOperatorType.Multiply:
257                     case CodeBinaryOperatorType.Divide:
258                     case CodeBinaryOperatorType.Modulus:
259                     case CodeBinaryOperatorType.BitwiseAnd:
260                     case CodeBinaryOperatorType.BitwiseOr:
261                         resultExprInfo = ArithmeticLiteral.ResultType(binaryExpr.Operator, lhsType, binaryExpr.Left, rhsType, binaryExpr.Right, validation, out error);
262                         if (resultExprInfo == null)
263                         {
264                             // check if constants are used with ulongs, as we should do some extra "conversions"
265                             if (((lhsType == typeof(ulong)) && (PromotionPossible(rhsType, binaryExpr.Right)))
266                                 || ((rhsType == typeof(ulong)) && (PromotionPossible(lhsType, binaryExpr.Left))))
267                             {
268                                 resultExprInfo = new RuleBinaryExpressionInfo(lhsType, rhsType, typeof(ulong));
269                             }
270                             else
271                             {
272                                 error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
273                                 validation.Errors.Add(error);
274                             }
275                         }
276                         break;
277 
278                     case CodeBinaryOperatorType.IdentityEquality:
279                     case CodeBinaryOperatorType.IdentityInequality:
280                         resultExprInfo = new RuleBinaryExpressionInfo(lhsType, rhsType, typeof(bool));
281                         break;
282 
283                     case CodeBinaryOperatorType.ValueEquality:
284                         resultExprInfo = Literal.AllowedComparison(lhsType, binaryExpr.Left, rhsType, binaryExpr.Right, binaryExpr.Operator, validation, out error);
285                         if (resultExprInfo == null)
286                         {
287                             // check if constants are used with ulongs, as we should do some extra "conversions"
288                             if (((lhsType == typeof(ulong)) && (PromotionPossible(rhsType, binaryExpr.Right)))
289                                 || ((rhsType == typeof(ulong)) && (PromotionPossible(lhsType, binaryExpr.Left))))
290                             {
291                                 resultExprInfo = new RuleBinaryExpressionInfo(lhsType, rhsType, typeof(bool));
292                             }
293                             else
294                             {
295                                 error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
296                                 validation.Errors.Add(error);
297                             }
298                         }
299                         break;
300 
301                     case CodeBinaryOperatorType.LessThan:
302                     case CodeBinaryOperatorType.LessThanOrEqual:
303                     case CodeBinaryOperatorType.GreaterThan:
304                     case CodeBinaryOperatorType.GreaterThanOrEqual:
305                         resultExprInfo = Literal.AllowedComparison(lhsType, binaryExpr.Left, rhsType, binaryExpr.Right, binaryExpr.Operator, validation, out error);
306                         if (resultExprInfo == null)
307                         {
308                             // check if constants are used with ulongs, as we should do some extra "conversions"
309                             if (((lhsType == typeof(ulong)) && (PromotionPossible(rhsType, binaryExpr.Right)))
310                                 || ((rhsType == typeof(ulong)) && (PromotionPossible(lhsType, binaryExpr.Left))))
311                             {
312                                 resultExprInfo = new RuleBinaryExpressionInfo(lhsType, rhsType, typeof(bool));
313                             }
314                             else
315                             {
316                                 error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
317                                 validation.Errors.Add(error);
318                             }
319                         }
320                         break;
321 
322                     case CodeBinaryOperatorType.BooleanAnd:
323                     case CodeBinaryOperatorType.BooleanOr:
324                         resultExprInfo = new RuleBinaryExpressionInfo(lhsType, rhsType, typeof(bool));
325                         if (lhsType != typeof(bool))
326                         {
327                             message = string.Format(CultureInfo.CurrentCulture, Messages.LogicalOpBadTypeLHS, binaryExpr.Operator.ToString(),
328                                 (lhsType == typeof(NullLiteral)) ? Messages.NullValue : RuleDecompiler.DecompileType(lhsType));
329                             error = new ValidationError(message, ErrorNumbers.Error_LeftOperandInvalidType);
330                             error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
331                             validation.Errors.Add(error);
332                             resultExprInfo = null;
333                         }
334                         if (rhsType != typeof(bool))
335                         {
336                             message = string.Format(CultureInfo.CurrentCulture, Messages.LogicalOpBadTypeRHS, binaryExpr.Operator.ToString(),
337                                 (rhsType == typeof(NullLiteral)) ? Messages.NullValue : RuleDecompiler.DecompileType(rhsType));
338                             error = new ValidationError(message, ErrorNumbers.Error_RightOperandInvalidType);
339                             error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
340                             validation.Errors.Add(error);
341                             resultExprInfo = null;
342                         }
343                         break;
344 
345                     default:
346                         {
347                             message = string.Format(CultureInfo.CurrentCulture, Messages.BinaryOpNotSupported, binaryExpr.Operator.ToString());
348                             error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
349                             error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
350                             validation.Errors.Add(error);
351                         }
352                         break;
353                 }
354             }
355 
356             // Validate any RuleAttributes, if present.
357             if (resultExprInfo != null)
358             {
359                 MethodInfo method = resultExprInfo.MethodInfo;
360                 if (method != null)
361                 {
362                     object[] attrs = method.GetCustomAttributes(typeof(RuleAttribute), true);
363                     if (attrs != null && attrs.Length > 0)
364                     {
365                         Stack<MemberInfo> methodStack = new Stack<MemberInfo>();
366                         methodStack.Push(method);
367 
368                         bool allAttributesValid = true;
369                         foreach (RuleAttribute ruleAttr in attrs)
370                         {
371                             if (!ruleAttr.Validate(validation, method, method.DeclaringType, method.GetParameters()))
372                                 allAttributesValid = false;
373                         }
374 
375                         methodStack.Pop();
376 
377                         if (!allAttributesValid)
378                             return null;
379                     }
380                 }
381             }
382 
383             return resultExprInfo;
384         }
385 
386         /// <summary>
387         /// Check that the expression is a constant, and is promotable to type ULONG.
388         /// </summary>
389         /// <param name="type"></param>
390         /// <param name="expression"></param>
391         /// <returns></returns>
392         [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
PromotionPossible(Type type, CodeExpression expression)393         private static bool PromotionPossible(Type type, CodeExpression expression)
394         {
395             // C# 2.0, section 6.1.6, int/long constants can be promoted to ulong as long as in range
396             if (type == typeof(int))
397             {
398                 CodePrimitiveExpression primitive = expression as CodePrimitiveExpression;
399                 if (primitive != null)
400                 {
401                     int i = (int)primitive.Value;
402                     return (i >= 0);
403                 }
404             }
405             else if (type == typeof(long))
406             {
407                 CodePrimitiveExpression primitive = expression as CodePrimitiveExpression;
408                 if (primitive != null)
409                 {
410                     long l = (long)primitive.Value;
411                     return (l >= 0);
412                 }
413             }
414             return false;
415         }
416         #endregion
417 
AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)418         internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
419         {
420             CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
421 
422             // Get the method info from the validation so we can look for [RuleRead] and [RuleWrite] attributes.
423             RuleBinaryExpressionInfo expressionInfo = analysis.Validation.ExpressionInfo(binaryExpr) as RuleBinaryExpressionInfo;
424             if (expressionInfo != null)
425             {
426                 // we may be calling a method, not a default operator
427                 MethodInfo method = expressionInfo.MethodInfo;
428                 if (method != null)
429                 {
430                     List<CodeExpression> attributedExprs = new List<CodeExpression>();
431                     CodeExpressionCollection arguments = new CodeExpressionCollection();
432                     arguments.Add(binaryExpr.Left);
433                     arguments.Add(binaryExpr.Right);
434                     CodeExpression targetObject = new CodeTypeReferenceExpression(method.DeclaringType);
435                     analysis.AnalyzeRuleAttributes(method, targetObject, qualifier, arguments, method.GetParameters(), attributedExprs);
436                 }
437             }
438 
439             // Analyze the left & right children.
440             RuleExpressionWalker.AnalyzeUsage(analysis, binaryExpr.Left, true, false, null);
441             RuleExpressionWalker.AnalyzeUsage(analysis, binaryExpr.Right, true, false, null);
442         }
443 
444         #region Evaluate
Evaluate(CodeExpression expression, RuleExecution execution)445         internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
446         {
447             CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
448 
449             object lhsValue = RuleExpressionWalker.Evaluate(execution, binaryExpr.Left).Value;
450             CodeBinaryOperatorType operation = binaryExpr.Operator;
451             // short-circuit ANDs and ORs
452             if (operation == CodeBinaryOperatorType.BooleanAnd)
453             {
454                 if ((bool)lhsValue)
455                 {
456                     // LHS is true, need to look at RHS
457                     object rhsValue = RuleExpressionWalker.Evaluate(execution, binaryExpr.Right).Value;
458                     return new RuleLiteralResult(rhsValue);
459                 }
460                 else
461                     // LHS is false, so result is false
462                     return new RuleLiteralResult(false);
463             }
464             else if (operation == CodeBinaryOperatorType.BooleanOr)
465             {
466                 if ((bool)lhsValue)
467                     // LHS is true, so result is true
468                     return new RuleLiteralResult(true);
469                 else
470                 {
471                     // LHS is false, so need to look at RHS
472                     object rhsValue = RuleExpressionWalker.Evaluate(execution, binaryExpr.Right).Value;
473                     return new RuleLiteralResult(rhsValue);
474                 }
475             }
476             else
477             {
478                 object resultValue;
479                 object rhsValue = RuleExpressionWalker.Evaluate(execution, binaryExpr.Right).Value;
480                 RuleBinaryExpressionInfo expressionInfo = execution.Validation.ExpressionInfo(binaryExpr) as RuleBinaryExpressionInfo;
481                 if (expressionInfo == null)  // Oops, someone forgot to validate.
482                 {
483                     string message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
484                     InvalidOperationException exception = new InvalidOperationException(message);
485                     exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
486                     throw exception;
487                 }
488                 MethodInfo methodInfo = expressionInfo.MethodInfo;
489                 if (methodInfo != null)
490                 {
491                     if (methodInfo == Literal.ObjectEquality)
492                     {
493                         resultValue = (lhsValue == rhsValue);
494                     }
495                     else
496                     {
497                         ParameterInfo[] existingParameters = methodInfo.GetParameters();
498                         object[] parameters = new object[2];
499                         parameters[0] = Executor.AdjustType(expressionInfo.LeftType, lhsValue, existingParameters[0].ParameterType);
500                         parameters[1] = Executor.AdjustType(expressionInfo.RightType, rhsValue, existingParameters[1].ParameterType);
501                         resultValue = methodInfo.Invoke(null, parameters);
502                     }
503                 }
504                 else
505                 {
506                     resultValue = EvaluateBinaryOperation(binaryExpr, expressionInfo.LeftType, lhsValue, operation, expressionInfo.RightType, rhsValue);
507                 }
508                 return new RuleLiteralResult(resultValue);
509             }
510         }
511 
512         [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
EvaluateBinaryOperation(CodeBinaryOperatorExpression binaryExpr, Type lhsType, object lhsValue, CodeBinaryOperatorType operation, Type rhsType, object rhsValue)513         private static object EvaluateBinaryOperation(CodeBinaryOperatorExpression binaryExpr, Type lhsType, object lhsValue, CodeBinaryOperatorType operation, Type rhsType, object rhsValue)
514         {
515             Literal leftLiteral;
516             Literal rightLiteral;
517             ArithmeticLiteral leftArithmetic;
518             ArithmeticLiteral rightArithmetic;
519             string message;
520             RuleEvaluationException exception;
521 
522             switch (operation)
523             {
524                 case CodeBinaryOperatorType.Add:
525                     leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
526                     if (leftArithmetic == null)
527                         break;
528                     rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
529                     if (rightArithmetic == null)
530                         break;
531                     return leftArithmetic.Add(rightArithmetic);
532                 case CodeBinaryOperatorType.Subtract:
533                     leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
534                     if (leftArithmetic == null)
535                         break;
536                     rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
537                     if (rightArithmetic == null)
538                         break;
539                     return leftArithmetic.Subtract(rightArithmetic);
540                 case CodeBinaryOperatorType.Multiply:
541                     leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
542                     if (leftArithmetic == null)
543                         break;
544                     rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
545                     if (rightArithmetic == null)
546                         break;
547                     return leftArithmetic.Multiply(rightArithmetic);
548                 case CodeBinaryOperatorType.Divide:
549                     leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
550                     if (leftArithmetic == null)
551                         break;
552                     rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
553                     if (rightArithmetic == null)
554                         break;
555                     return leftArithmetic.Divide(rightArithmetic);
556                 case CodeBinaryOperatorType.Modulus:
557                     leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
558                     if (leftArithmetic == null)
559                         break;
560                     rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
561                     if (rightArithmetic == null)
562                         break;
563                     return leftArithmetic.Modulus(rightArithmetic);
564                 case CodeBinaryOperatorType.BitwiseAnd:
565                     leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
566                     if (leftArithmetic == null)
567                         break;
568                     rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
569                     if (rightArithmetic == null)
570                         break;
571                     return leftArithmetic.BitAnd(rightArithmetic);
572                 case CodeBinaryOperatorType.BitwiseOr:
573                     leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
574                     if (leftArithmetic == null)
575                         break;
576                     rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
577                     if (rightArithmetic == null)
578                         break;
579                     return leftArithmetic.BitOr(rightArithmetic);
580 
581                 case CodeBinaryOperatorType.ValueEquality:
582                     leftLiteral = Literal.MakeLiteral(lhsType, lhsValue);
583                     if (leftLiteral == null)
584                         break;
585                     rightLiteral = Literal.MakeLiteral(rhsType, rhsValue);
586                     if (rightLiteral == null)
587                         break;
588                     return leftLiteral.Equal(rightLiteral);
589                 case CodeBinaryOperatorType.IdentityEquality:
590                     return lhsValue == rhsValue;
591                 case CodeBinaryOperatorType.IdentityInequality:
592                     return lhsValue != rhsValue;
593 
594                 case CodeBinaryOperatorType.LessThan:
595                     leftLiteral = Literal.MakeLiteral(lhsType, lhsValue);
596                     if (leftLiteral == null)
597                         break;
598                     rightLiteral = Literal.MakeLiteral(rhsType, rhsValue);
599                     if (rightLiteral == null)
600                         break;
601                     return leftLiteral.LessThan(rightLiteral);
602                 case CodeBinaryOperatorType.LessThanOrEqual:
603                     leftLiteral = Literal.MakeLiteral(lhsType, lhsValue);
604                     if (leftLiteral == null)
605                         break;
606                     rightLiteral = Literal.MakeLiteral(rhsType, rhsValue);
607                     if (rightLiteral == null)
608                         break;
609                     return leftLiteral.LessThanOrEqual(rightLiteral);
610                 case CodeBinaryOperatorType.GreaterThan:
611                     leftLiteral = Literal.MakeLiteral(lhsType, lhsValue);
612                     if (leftLiteral == null)
613                         break;
614                     rightLiteral = Literal.MakeLiteral(rhsType, rhsValue);
615                     if (rightLiteral == null)
616                         break;
617                     return leftLiteral.GreaterThan(rightLiteral);
618                 case CodeBinaryOperatorType.GreaterThanOrEqual:
619                     leftLiteral = Literal.MakeLiteral(lhsType, lhsValue);
620                     if (leftLiteral == null)
621                         break;
622                     rightLiteral = Literal.MakeLiteral(rhsType, rhsValue);
623                     if (rightLiteral == null)
624                         break;
625                     return leftLiteral.GreaterThanOrEqual(rightLiteral);
626 
627                 default:
628                     // should never happen
629                     // BooleanAnd & BooleanOr short-circuited before call
630                     // Assign disallowed at validation time
631                     message = string.Format(CultureInfo.CurrentCulture, Messages.BinaryOpNotSupported, operation.ToString());
632                     exception = new RuleEvaluationException(message);
633                     exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
634                     throw exception;
635             }
636 
637             message = string.Format(CultureInfo.CurrentCulture,
638                 Messages.BinaryOpFails,
639                 operation.ToString(),
640                 RuleDecompiler.DecompileType(lhsType),
641                 RuleDecompiler.DecompileType(rhsType));
642             exception = new RuleEvaluationException(message);
643             exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
644             throw exception;
645         }
646         #endregion
647 
648         #region Decompile
649 
650         [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)651         internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
652         {
653             bool mustParenthesize = false;
654             CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
655 
656             if (binaryExpr.Left == null)
657             {
658                 string message = string.Format(CultureInfo.CurrentCulture, Messages.NullBinaryOpLHS, binaryExpr.Operator.ToString());
659                 RuleEvaluationException exception = new RuleEvaluationException(message);
660                 exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
661                 throw exception;
662             }
663             if (binaryExpr.Right == null)
664             {
665                 string message = string.Format(CultureInfo.CurrentCulture, Messages.NullBinaryOpRHS, binaryExpr.Operator.ToString());
666                 RuleEvaluationException exception = new RuleEvaluationException(message);
667                 exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
668                 throw exception;
669             }
670 
671             string opString;
672 
673             switch (binaryExpr.Operator)
674             {
675                 case CodeBinaryOperatorType.Modulus:
676                     opString = " % ";
677                     break;
678                 case CodeBinaryOperatorType.Multiply:
679                     opString = " * ";
680                     break;
681                 case CodeBinaryOperatorType.Divide:
682                     opString = " / ";
683                     break;
684 
685                 case CodeBinaryOperatorType.Subtract:
686                     opString = " - ";
687                     break;
688                 case CodeBinaryOperatorType.Add:
689                     opString = " + ";
690                     break;
691 
692                 case CodeBinaryOperatorType.LessThan:
693                     opString = " < ";
694                     break;
695                 case CodeBinaryOperatorType.LessThanOrEqual:
696                     opString = " <= ";
697                     break;
698                 case CodeBinaryOperatorType.GreaterThan:
699                     opString = " > ";
700                     break;
701                 case CodeBinaryOperatorType.GreaterThanOrEqual:
702                     opString = " >= ";
703                     break;
704 
705                 case CodeBinaryOperatorType.IdentityEquality:
706                 case CodeBinaryOperatorType.ValueEquality:
707                     opString = " == ";
708                     break;
709                 case CodeBinaryOperatorType.IdentityInequality:
710                     opString = " != ";
711                     break;
712 
713                 case CodeBinaryOperatorType.BitwiseAnd:
714                     opString = " & ";
715                     break;
716 
717                 case CodeBinaryOperatorType.BitwiseOr:
718                     opString = " | ";
719                     break;
720 
721                 case CodeBinaryOperatorType.BooleanAnd:
722                     opString = " && ";
723                     break;
724 
725                 case CodeBinaryOperatorType.BooleanOr:
726                     opString = " || ";
727                     break;
728 
729                 default:
730                     string message = string.Format(CultureInfo.CurrentCulture, Messages.BinaryOpNotSupported, binaryExpr.Operator.ToString());
731                     NotSupportedException exception = new NotSupportedException(message);
732                     exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
733                     throw exception;
734             }
735 
736             CodeExpression leftExpr = binaryExpr.Left;
737             CodeExpression rightExpr = binaryExpr.Right;
738 
739 
740             if (binaryExpr.Operator == CodeBinaryOperatorType.ValueEquality)
741             {
742                 // Look for special cases:
743                 //    LHS == false              --> ! LHS
744                 // or
745                 //    (LHS == expr) == false    --> LHS != expr
746 
747                 CodePrimitiveExpression rhsPrimitive = rightExpr as CodePrimitiveExpression;
748                 if (rhsPrimitive != null)
749                 {
750                     object rhsValue = rhsPrimitive.Value;
751                     if (rhsValue != null)
752                     {
753                         // we don't have the comparison "==null"
754                         if (rhsValue.GetType() == typeof(bool) && (bool)rhsValue == false)
755                         {
756                             // We have comparison "== false".
757 
758                             CodeBinaryOperatorExpression lhsBinary = leftExpr as CodeBinaryOperatorExpression;
759                             if (lhsBinary != null && lhsBinary.Operator == CodeBinaryOperatorType.ValueEquality)
760                             {
761                                 // We have the pattern
762                                 //      (expr1 == expr2) == false
763                                 // Treat this as:
764                                 //      expr1 != expr2
765 
766                                 opString = " != ";
767 
768                                 leftExpr = lhsBinary.Left;
769                                 rightExpr = lhsBinary.Right;
770                             }
771                             else
772                             {
773                                 // We have the pattern
774                                 //      LHS == false
775                                 // Treat this as:
776                                 //      ! LHS
777 
778                                 mustParenthesize = RuleDecompiler.MustParenthesize(leftExpr, parentExpression);
779                                 if (mustParenthesize)
780                                     stringBuilder.Append("(");
781 
782                                 // Note the "parentExpression" passed to the child decompile... cast is the only
783                                 // built-in operation that has "unary" precedence, so pass that as the parent
784                                 // to get the parenthesization right. .
785                                 stringBuilder.Append("!");
786                                 RuleExpressionWalker.Decompile(stringBuilder, leftExpr, new CodeCastExpression());
787 
788                                 if (mustParenthesize)
789                                     stringBuilder.Append(")");
790 
791                                 return;
792                             }
793                         }
794                     }
795                 }
796             }
797             else if (binaryExpr.Operator == CodeBinaryOperatorType.Subtract)
798             {
799                 // Look for the special case:
800                 //    0 - RHS       --> - RHS
801 
802                 CodePrimitiveExpression lhsPrimitive = leftExpr as CodePrimitiveExpression;
803                 if (lhsPrimitive != null && lhsPrimitive.Value != null)
804                 {
805                     object lhsValue = lhsPrimitive.Value;
806 
807                     // Check if the LHS is zero.  We'll only check a few types (decimal,
808                     // double, float, int, long), since these occur most often (and the
809                     // unsigned types are all illegal).
810                     TypeCode tc = Type.GetTypeCode(lhsValue.GetType());
811                     bool isZero = false;
812                     switch (tc)
813                     {
814                         case TypeCode.Decimal:
815                             isZero = ((decimal)lhsValue) == 0;
816                             break;
817 
818                         case TypeCode.Double:
819                             isZero = ((double)lhsValue) == 0;
820                             break;
821 
822                         case TypeCode.Single:
823                             isZero = ((float)lhsValue) == 0;
824                             break;
825 
826                         case TypeCode.Int32:
827                             isZero = ((int)lhsValue) == 0;
828                             break;
829 
830                         case TypeCode.Int64:
831                             isZero = ((long)lhsValue) == 0;
832                             break;
833                     }
834 
835                     if (isZero)
836                     {
837                         mustParenthesize = RuleDecompiler.MustParenthesize(rightExpr, parentExpression);
838                         if (mustParenthesize)
839                             stringBuilder.Append("(");
840 
841                         // Note the "parentExpression" passed to the child decompile... cast is the only
842                         // built-in operation that has "unary" precedence, so pass that as the parent
843                         // to get the parenthesization right.
844                         stringBuilder.Append("-");
845                         RuleExpressionWalker.Decompile(stringBuilder, rightExpr, new CodeCastExpression());
846 
847                         if (mustParenthesize)
848                             stringBuilder.Append(")");
849 
850                         return;
851                     }
852                 }
853             }
854 
855             mustParenthesize = RuleDecompiler.MustParenthesize(binaryExpr, parentExpression);
856             if (mustParenthesize)
857                 stringBuilder.Append("(");
858 
859             RuleExpressionWalker.Decompile(stringBuilder, leftExpr, binaryExpr);
860             stringBuilder.Append(opString);
861             RuleExpressionWalker.Decompile(stringBuilder, rightExpr, binaryExpr);
862 
863             if (mustParenthesize)
864                 stringBuilder.Append(")");
865         }
866         #endregion
867 
Clone(CodeExpression expression)868         internal override CodeExpression Clone(CodeExpression expression)
869         {
870             CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
871 
872             CodeBinaryOperatorExpression newOp = new CodeBinaryOperatorExpression();
873             newOp.Operator = binaryExpr.Operator;
874             newOp.Left = RuleExpressionWalker.Clone(binaryExpr.Left);
875             newOp.Right = RuleExpressionWalker.Clone(binaryExpr.Right);
876             return newOp;
877         }
878 
Match(CodeExpression expression, CodeExpression comperand)879         internal override bool Match(CodeExpression expression, CodeExpression comperand)
880         {
881             CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
882 
883             CodeBinaryOperatorExpression comperandBinary = (CodeBinaryOperatorExpression)comperand;
884             return (binaryExpr.Operator == comperandBinary.Operator
885                 && RuleExpressionWalker.Match(binaryExpr.Left, comperandBinary.Left)
886                 && RuleExpressionWalker.Match(binaryExpr.Right, comperandBinary.Right));
887         }
888     }
889 
890     #endregion
891 
892     #region Field ref expression
893 
894     // CodeFieldReferenceExpression
895     internal class FieldReferenceExpression : RuleExpressionInternal
896     {
Validate(CodeExpression expression, RuleValidation validation, bool isWritten)897         internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
898         {
899             string message;
900 
901             CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
902 
903             if (fieldRefExpr.TargetObject == null)
904             {
905                 message = string.Format(CultureInfo.CurrentCulture, Messages.NullFieldTarget, fieldRefExpr.FieldName);
906                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
907                 error.UserData[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
908                 validation.Errors.Add(error);
909                 return null;
910             }
911 
912             // Early exit from this if a cycle is detected.
913             if (!validation.PushParentExpression(fieldRefExpr))
914                 return null;
915 
916             RuleExpressionInfo targetExprInfo = RuleExpressionWalker.Validate(validation, fieldRefExpr.TargetObject, false);
917 
918             validation.PopParentExpression();
919 
920             if (targetExprInfo == null)     // error occurred, so simply return
921                 return null;
922 
923             Type targetType = targetExprInfo.ExpressionType;
924             if (targetType == null)         // no type, so must have been an error already
925                 return null;
926 
927             if (targetType == typeof(NullLiteral))
928             {
929                 message = string.Format(CultureInfo.CurrentCulture, Messages.NullFieldTarget, fieldRefExpr.FieldName);
930                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_BindingTypeMissing);
931                 error.UserData[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
932                 validation.Errors.Add(error);
933                 return null;
934             }
935 
936             BindingFlags bindingFlags = BindingFlags.Public;
937             if (fieldRefExpr.TargetObject is CodeTypeReferenceExpression)
938                 bindingFlags |= BindingFlags.Static | BindingFlags.FlattenHierarchy;
939             else
940                 bindingFlags |= BindingFlags.Instance;
941             if (validation.AllowInternalMembers(targetType))
942                 bindingFlags |= BindingFlags.NonPublic;
943 
944             FieldInfo fi = targetType.GetField(fieldRefExpr.FieldName, bindingFlags);
945             if (fi == null)
946             {
947                 message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownField, fieldRefExpr.FieldName, RuleDecompiler.DecompileType(targetType));
948                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
949                 error.UserData[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
950                 validation.Errors.Add(error);
951                 return null;
952             }
953 
954             if (fi.FieldType == null)
955             {
956                 // This can only happen with a design-time type.
957                 message = string.Format(CultureInfo.CurrentCulture, Messages.CouldNotDetermineMemberType, fieldRefExpr.FieldName);
958                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_CouldNotDetermineMemberType);
959                 error.UserData[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
960                 validation.Errors.Add(error);
961                 return null;
962             }
963 
964             if (isWritten && fi.IsLiteral)
965             {
966                 message = string.Format(CultureInfo.CurrentCulture, Messages.FieldSetNotAllowed, fieldRefExpr.FieldName, RuleDecompiler.DecompileType(targetType));
967                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
968                 error.UserData[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
969                 validation.Errors.Add(error);
970                 return null;
971             }
972 
973             if (!validation.ValidateMemberAccess(fieldRefExpr.TargetObject, targetType, fi, fi.Name, fieldRefExpr))
974                 return null;
975 
976             // Is it possible to set fi by validation.ResolveFieldOrProperty(targetType, fieldExpr.FieldName)?
977             validation.IsAuthorized(fi.FieldType);
978             return new RuleFieldExpressionInfo(fi);
979         }
980 
AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)981         internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
982         {
983             CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
984             CodeExpression targetObject = fieldRefExpr.TargetObject;
985             RuleExpressionWalker.AnalyzeUsage(analysis, targetObject, isRead, isWritten, new RulePathQualifier(fieldRefExpr.FieldName, qualifier));
986         }
987 
Evaluate(CodeExpression expression, RuleExecution execution)988         internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
989         {
990             CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
991             object target = RuleExpressionWalker.Evaluate(execution, fieldRefExpr.TargetObject).Value;
992 
993             RuleFieldExpressionInfo fieldExprInfo = execution.Validation.ExpressionInfo(fieldRefExpr) as RuleFieldExpressionInfo;
994             if (fieldExprInfo == null)  // Oops, someone forgot to validate.
995             {
996                 string message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
997                 InvalidOperationException exception = new InvalidOperationException(message);
998                 exception.Data[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
999                 throw exception;
1000             }
1001 
1002             FieldInfo fi = fieldExprInfo.FieldInfo;
1003 
1004             return new RuleFieldResult(target, fi);
1005         }
1006 
Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)1007         internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
1008         {
1009             CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
1010 
1011             CodeExpression targetObject = fieldRefExpr.TargetObject;
1012             if (targetObject == null)
1013             {
1014                 string message = string.Format(CultureInfo.CurrentCulture, Messages.NullFieldTarget, fieldRefExpr.FieldName);
1015                 RuleEvaluationException exception = new RuleEvaluationException(message);
1016                 exception.Data[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
1017                 throw exception;
1018             }
1019 
1020             RuleExpressionWalker.Decompile(stringBuilder, targetObject, fieldRefExpr);
1021             stringBuilder.Append('.');
1022             stringBuilder.Append(fieldRefExpr.FieldName);
1023         }
1024 
Clone(CodeExpression expression)1025         internal override CodeExpression Clone(CodeExpression expression)
1026         {
1027             CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
1028 
1029             CodeFieldReferenceExpression newField = new CodeFieldReferenceExpression();
1030             newField.FieldName = fieldRefExpr.FieldName;
1031             newField.TargetObject = RuleExpressionWalker.Clone(fieldRefExpr.TargetObject);
1032             return newField;
1033         }
1034 
Match(CodeExpression expression, CodeExpression comperand)1035         internal override bool Match(CodeExpression expression, CodeExpression comperand)
1036         {
1037             CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
1038 
1039             CodeFieldReferenceExpression newField = (CodeFieldReferenceExpression)comperand;
1040             return (fieldRefExpr.FieldName == newField.FieldName
1041                 && RuleExpressionWalker.Match(fieldRefExpr.TargetObject, newField.TargetObject));
1042         }
1043     }
1044 
1045     #endregion
1046 
1047     #region Property ref expression
1048 
1049     // CodePropertyReferenceExpression
1050     internal class PropertyReferenceExpression : RuleExpressionInternal
1051     {
Validate(CodeExpression expression, RuleValidation validation, bool isWritten)1052         internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
1053         {
1054             string message;
1055 
1056             CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
1057 
1058             if (propGetExpr.TargetObject == null)
1059             {
1060                 message = string.Format(CultureInfo.CurrentCulture, Messages.NullPropertyTarget, propGetExpr.PropertyName);
1061                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
1062                 error.UserData[RuleUserDataKeys.ErrorObject] = propGetExpr;
1063                 validation.Errors.Add(error);
1064                 return null;
1065             }
1066 
1067             // Early exit from this if a cycle is detected.
1068             if (!validation.PushParentExpression(propGetExpr))
1069                 return null;
1070 
1071             RuleExpressionInfo targetExprInfo = RuleExpressionWalker.Validate(validation, propGetExpr.TargetObject, false);
1072 
1073             validation.PopParentExpression();
1074 
1075             if (targetExprInfo == null)     // error occurred, so simply return
1076                 return null;
1077 
1078             Type targetType = targetExprInfo.ExpressionType;
1079             if (targetType == null)         // no type, so must have been an error already
1080                 return null;
1081 
1082             if (targetType == typeof(NullLiteral))
1083             {
1084                 message = string.Format(CultureInfo.CurrentCulture, Messages.NullPropertyTarget, propGetExpr.PropertyName);
1085                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_BindingTypeMissing);
1086                 error.UserData[RuleUserDataKeys.ErrorObject] = propGetExpr;
1087                 validation.Errors.Add(error);
1088                 return null;
1089             }
1090 
1091             bool includeNonPublic = false;
1092             BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy;
1093             if (validation.AllowInternalMembers(targetType))
1094             {
1095                 bindingFlags |= BindingFlags.NonPublic;
1096                 includeNonPublic = true;
1097             }
1098             PropertyInfo pi = validation.ResolveProperty(targetType, propGetExpr.PropertyName, bindingFlags);
1099 
1100             if (pi == null)
1101             {
1102                 message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownProperty, propGetExpr.PropertyName, RuleDecompiler.DecompileType(targetType));
1103                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
1104                 error.UserData[RuleUserDataKeys.ErrorObject] = propGetExpr;
1105                 validation.Errors.Add(error);
1106                 return null;
1107             }
1108 
1109             if (pi.PropertyType == null)
1110             {
1111                 // This can only happen with a design-time type.
1112                 message = string.Format(CultureInfo.CurrentCulture, Messages.CouldNotDetermineMemberType, propGetExpr.PropertyName);
1113                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_CouldNotDetermineMemberType);
1114                 error.UserData[RuleUserDataKeys.ErrorObject] = propGetExpr;
1115                 validation.Errors.Add(error);
1116                 return null;
1117             }
1118 
1119             MethodInfo accessorMethod = isWritten ? pi.GetSetMethod(includeNonPublic) : pi.GetGetMethod(includeNonPublic);
1120             if (accessorMethod == null)
1121             {
1122                 string baseMessage = isWritten ? Messages.UnknownPropertySet : Messages.UnknownPropertyGet;
1123                 message = string.Format(CultureInfo.CurrentCulture, baseMessage, propGetExpr.PropertyName, RuleDecompiler.DecompileType(targetType));
1124                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
1125                 error.UserData[RuleUserDataKeys.ErrorObject] = propGetExpr;
1126                 validation.Errors.Add(error);
1127                 return null;
1128             }
1129 
1130             if (!validation.ValidateMemberAccess(propGetExpr.TargetObject, targetType, accessorMethod, propGetExpr.PropertyName, propGetExpr))
1131                 return null;
1132 
1133             // Validate any RuleAttributes, if present.
1134             object[] attrs = pi.GetCustomAttributes(typeof(RuleAttribute), true);
1135             if (attrs != null && attrs.Length > 0)
1136             {
1137                 Stack<MemberInfo> methodStack = new Stack<MemberInfo>();
1138                 methodStack.Push(pi);
1139 
1140                 bool allAttributesValid = true;
1141                 foreach (RuleAttribute ruleAttr in attrs)
1142                 {
1143                     if (!ruleAttr.Validate(validation, pi, targetType, null))
1144                         allAttributesValid = false;
1145                 }
1146 
1147                 methodStack.Pop();
1148 
1149                 if (!allAttributesValid)
1150                     return null;
1151             }
1152 
1153             return new RulePropertyExpressionInfo(pi, pi.PropertyType, false);
1154         }
1155 
1156 
AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)1157         internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
1158         {
1159             string message;
1160 
1161             CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
1162 
1163             // Evaluate the target object and get its type.
1164             CodeExpression targetObject = propGetExpr.TargetObject;
1165             RuleExpressionInfo targetExprInfo = analysis.Validation.ExpressionInfo(targetObject);
1166             if (targetExprInfo == null)  // Oops, someone forgot to validate.
1167             {
1168                 message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
1169                 InvalidOperationException exception = new InvalidOperationException(message);
1170                 exception.Data[RuleUserDataKeys.ErrorObject] = targetObject;
1171                 throw exception;
1172             }
1173 
1174             // Get the property info from the validator so we can look for [RuleRead] and [RuleWrite] attributes.
1175             RulePropertyExpressionInfo propExprInfo = analysis.Validation.ExpressionInfo(propGetExpr) as RulePropertyExpressionInfo;
1176             if (propExprInfo == null)  // Oops, someone forgot to validate.
1177             {
1178                 message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
1179                 InvalidOperationException exception = new InvalidOperationException(message);
1180                 exception.Data[RuleUserDataKeys.ErrorObject] = propGetExpr;
1181                 throw exception;
1182             }
1183 
1184             PropertyInfo pi = propExprInfo.PropertyInfo;
1185 
1186             // Look for RuleAttribute's on the invoked property.
1187             List<CodeExpression> attributedExprs = new List<CodeExpression>();
1188             analysis.AnalyzeRuleAttributes(pi, targetObject, qualifier, null, null, attributedExprs);
1189 
1190             // See if the target object needs default analysis.
1191             if (!attributedExprs.Contains(targetObject))
1192             {
1193                 // The property had no [RuleRead] or [RuleWrite] attributes.  Just qualify the target object with
1194                 // the property name and proceed with the analysis.
1195                 RuleExpressionWalker.AnalyzeUsage(analysis, targetObject, isRead, isWritten, new RulePathQualifier(pi.Name, qualifier));
1196             }
1197         }
1198 
Evaluate(CodeExpression expression, RuleExecution execution)1199         internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
1200         {
1201             CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
1202 
1203             object target = RuleExpressionWalker.Evaluate(execution, propGetExpr.TargetObject).Value;
1204 
1205             RulePropertyExpressionInfo propExprInfo = execution.Validation.ExpressionInfo(propGetExpr) as RulePropertyExpressionInfo;
1206             if (propExprInfo == null)  // Oops, someone forgot to validate.
1207             {
1208                 string message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
1209                 InvalidOperationException exception = new InvalidOperationException(message);
1210                 exception.Data[RuleUserDataKeys.ErrorObject] = propGetExpr;
1211                 throw exception;
1212             }
1213 
1214             PropertyInfo pi = propExprInfo.PropertyInfo;
1215             return new RulePropertyResult(pi, target, null);
1216         }
1217 
Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)1218         internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
1219         {
1220             CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
1221 
1222             CodeExpression targetObject = propGetExpr.TargetObject;
1223             if (targetObject == null)
1224             {
1225                 string message = string.Format(CultureInfo.CurrentCulture, Messages.NullPropertyTarget, propGetExpr.PropertyName);
1226                 RuleEvaluationException exception = new RuleEvaluationException(message);
1227                 exception.Data[RuleUserDataKeys.ErrorObject] = propGetExpr;
1228                 throw exception;
1229             }
1230 
1231             RuleExpressionWalker.Decompile(stringBuilder, targetObject, propGetExpr);
1232             stringBuilder.Append('.');
1233             stringBuilder.Append(propGetExpr.PropertyName);
1234         }
1235 
Clone(CodeExpression expression)1236         internal override CodeExpression Clone(CodeExpression expression)
1237         {
1238             CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
1239 
1240             CodePropertyReferenceExpression newProperty = new CodePropertyReferenceExpression();
1241             newProperty.PropertyName = propGetExpr.PropertyName;
1242             newProperty.TargetObject = RuleExpressionWalker.Clone(propGetExpr.TargetObject);
1243             return newProperty;
1244         }
1245 
Match(CodeExpression expression, CodeExpression comperand)1246         internal override bool Match(CodeExpression expression, CodeExpression comperand)
1247         {
1248             CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
1249 
1250             CodePropertyReferenceExpression newProperty = (CodePropertyReferenceExpression)comperand;
1251             return (propGetExpr.PropertyName == newProperty.PropertyName
1252                 && RuleExpressionWalker.Match(propGetExpr.TargetObject, newProperty.TargetObject));
1253         }
1254     }
1255 
1256     #endregion
1257 
1258     #region Method invoke expression
1259 
1260     // CodeMethodInvokeExpression
1261     internal class MethodInvokeExpression : RuleExpressionInternal
1262     {
1263         [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
Validate(CodeExpression expression, RuleValidation validation, bool isWritten)1264         internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
1265         {
1266             Type targetType = null;
1267             RuleMethodInvokeExpressionInfo methodInvokeInfo = null;
1268             string message;
1269             ValidationError error = null;
1270             BindingFlags bindingFlags = BindingFlags.Public;
1271 
1272             CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
1273 
1274             if (isWritten)
1275             {
1276                 message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeMethodInvokeExpression).ToString());
1277                 error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
1278                 error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
1279                 validation.Errors.Add(error);
1280                 return null;
1281             }
1282 
1283             if ((invokeExpr.Method == null) || (invokeExpr.Method.TargetObject == null))
1284             {
1285                 message = string.Format(CultureInfo.CurrentCulture, Messages.NullMethodTarget, invokeExpr.Method.MethodName);
1286                 error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
1287                 error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
1288                 validation.Errors.Add(error);
1289                 return null; // Fatal error; discontinue validation of this object.
1290             }
1291 
1292             if ((invokeExpr.Method.TypeArguments != null) && (invokeExpr.Method.TypeArguments.Count > 0))
1293             {
1294                 error = new ValidationError(Messages.GenericMethodsNotSupported, ErrorNumbers.Error_CodeExpressionNotHandled);
1295                 error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
1296                 validation.Errors.Add(error);
1297                 return null;
1298             }
1299 
1300             try
1301             {
1302                 // Early exit from this if a cycle is detected.
1303                 if (!validation.PushParentExpression(invokeExpr))
1304                     return null;
1305 
1306                 RuleExpressionInfo targetExprInfo = RuleExpressionWalker.Validate(validation, invokeExpr.Method.TargetObject, false);
1307                 if (targetExprInfo == null)     // error occurred, so simply return
1308                     return null;
1309 
1310                 targetType = targetExprInfo.ExpressionType;
1311                 if (targetType == null)
1312                     return null;
1313 
1314                 // if an error occurred (targetType == null), continue on to validate the arguments
1315                 if (targetType == typeof(NullLiteral))
1316                 {
1317                     message = string.Format(CultureInfo.CurrentCulture, Messages.NullMethodTarget, invokeExpr.Method.MethodName);
1318                     error = new ValidationError(message, ErrorNumbers.Error_BindingTypeMissing);
1319                     error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
1320                     validation.Errors.Add(error);
1321                     targetType = null; // force exit after validating the arguments
1322                 }
1323 
1324                 List<CodeExpression> argExprs = new List<CodeExpression>();
1325 
1326                 bool hasInvalidArgument = false;
1327                 if (invokeExpr.Parameters != null)
1328                 {
1329                     for (int i = 0; i < invokeExpr.Parameters.Count; ++i)
1330                     {
1331                         CodeExpression argExpr = invokeExpr.Parameters[i];
1332                         if (argExpr == null)
1333                         {
1334                             message = string.Format(CultureInfo.CurrentCulture, Messages.NullMethodParameter, i.ToString(CultureInfo.CurrentCulture), invokeExpr.Method.MethodName);
1335                             error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
1336                             error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
1337                             validation.Errors.Add(error);
1338                             targetType = null; // force exit after validating the rest of the arguments
1339                         }
1340                         else
1341                         {
1342                             if (argExpr is CodeTypeReferenceExpression)
1343                             {
1344                                 message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, argExpr.GetType().FullName);
1345                                 error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
1346                                 error.UserData[RuleUserDataKeys.ErrorObject] = argExpr;
1347                                 validation.AddError(error);
1348 
1349                                 hasInvalidArgument = true;
1350                             }
1351 
1352                             // Validate the argument.
1353                             RuleExpressionInfo argExprInfo = RuleExpressionWalker.Validate(validation, argExpr, false);
1354                             if (argExprInfo == null)
1355                                 hasInvalidArgument = true;
1356                             argExprs.Add(argExpr);
1357                         }
1358                     }
1359                 }
1360 
1361                 // Stop further validation if there was a problem with the target expression.
1362                 if (targetType == null)
1363                     return null;
1364 
1365                 // Stop further validation if there was a problem with any of the arguments.
1366                 if (hasInvalidArgument)
1367                     return null;
1368 
1369                 if (invokeExpr.Method.TargetObject is CodeTypeReferenceExpression)
1370                     bindingFlags |= BindingFlags.Static | BindingFlags.FlattenHierarchy;
1371                 else
1372                     bindingFlags |= BindingFlags.Instance;
1373                 if (validation.AllowInternalMembers(targetType))
1374                     bindingFlags |= BindingFlags.NonPublic;
1375 
1376                 // Everything okay so far, try to resolve the method.
1377                 methodInvokeInfo = validation.ResolveMethod(targetType, invokeExpr.Method.MethodName, bindingFlags, argExprs, out error);
1378                 if ((methodInvokeInfo == null) && (invokeExpr.UserData.Contains(RuleUserDataKeys.QualifiedName)))
1379                 {
1380                     // failed to resolve the method, but a fully qualified type name is around
1381                     // load the type, add it to the assemblies, and try again
1382                     string qualifiedName = invokeExpr.UserData[RuleUserDataKeys.QualifiedName] as string;
1383                     Type containingClassType = validation.ResolveType(qualifiedName);
1384                     if (containingClassType != null)
1385                     {
1386                         validation.DetermineExtensionMethods(containingClassType.Assembly);
1387                         methodInvokeInfo = validation.ResolveMethod(targetType, invokeExpr.Method.MethodName, bindingFlags, argExprs, out error);
1388                     }
1389                 }
1390                 if (methodInvokeInfo == null)
1391                 {
1392                     error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
1393                     validation.Errors.Add(error);
1394                     return null;
1395                 }
1396             }
1397             finally
1398             {
1399                 validation.PopParentExpression();
1400             }
1401 
1402 
1403             MethodInfo mi = methodInvokeInfo.MethodInfo;
1404 
1405             if (mi.ReturnType == null)
1406             {
1407                 // This can only happen with a design-time type.
1408                 message = string.Format(CultureInfo.CurrentCulture, Messages.CouldNotDetermineMemberType, invokeExpr.Method.MethodName);
1409                 error = new ValidationError(message, ErrorNumbers.Error_CouldNotDetermineMemberType);
1410                 error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
1411                 validation.Errors.Add(error);
1412                 return null;
1413             }
1414 
1415             if (!validation.ValidateMemberAccess(invokeExpr.Method.TargetObject, targetType, mi, invokeExpr.Method.MethodName, invokeExpr))
1416                 return null;
1417 
1418             // Validate any RuleAttributes, if present.
1419             object[] attrs = mi.GetCustomAttributes(typeof(RuleAttribute), true);
1420             if (attrs != null && attrs.Length > 0)
1421             {
1422                 Stack<MemberInfo> methodStack = new Stack<MemberInfo>();
1423                 methodStack.Push(mi);
1424 
1425                 bool allAttributesValid = true;
1426                 foreach (RuleAttribute ruleAttr in attrs)
1427                 {
1428                     if (!ruleAttr.Validate(validation, mi, targetType, mi.GetParameters()))
1429                         allAttributesValid = false;
1430                 }
1431 
1432                 methodStack.Pop();
1433 
1434                 if (!allAttributesValid)
1435                     return null;
1436             }
1437 
1438             // if this is an extension method, save the type information
1439             if (mi is ExtensionMethodInfo)
1440             {
1441                 invokeExpr.UserData[RuleUserDataKeys.QualifiedName] = mi.DeclaringType.AssemblyQualifiedName;
1442             }
1443 
1444             return methodInvokeInfo;
1445         }
1446 
AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)1447         internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
1448         {
1449             string message;
1450 
1451             CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
1452 
1453             // Get the target object's type.
1454             CodeExpression targetObject = invokeExpr.Method.TargetObject;
1455             RuleExpressionInfo targetExprInfo = analysis.Validation.ExpressionInfo(targetObject);
1456             if (targetExprInfo == null)  // Oops, someone forgot to validate.
1457             {
1458                 message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
1459                 InvalidOperationException exception = new InvalidOperationException(message);
1460                 exception.Data[RuleUserDataKeys.ErrorObject] = targetObject;
1461                 throw exception;
1462             }
1463 
1464             // Get the method info from the validation so we can look for [RuleRead] and [RuleWrite] attributes.
1465             RuleMethodInvokeExpressionInfo methodExprInfo = analysis.Validation.ExpressionInfo(invokeExpr) as RuleMethodInvokeExpressionInfo;
1466             if (methodExprInfo == null)  // Oops, someone forgot to validate.
1467             {
1468                 message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
1469                 InvalidOperationException exception = new InvalidOperationException(message);
1470                 exception.Data[RuleUserDataKeys.ErrorObject] = invokeExpr;
1471                 throw exception;
1472             }
1473 
1474             MethodInfo mi = methodExprInfo.MethodInfo;
1475 
1476             // Look for RuleAttribute's on the invoked method.
1477             List<CodeExpression> attributedExprs = new List<CodeExpression>();
1478             analysis.AnalyzeRuleAttributes(mi, targetObject, qualifier, invokeExpr.Parameters, mi.GetParameters(), attributedExprs);
1479 
1480             // See if the target object needs default analysis.
1481             if (!attributedExprs.Contains(targetObject))
1482             {
1483                 // No applicable [RuleRead] or [RuleWrite] attributes were found on the target object.
1484 
1485                 // If we're analyzing for dependencies, assume that this method uses the
1486                 // value of the target object, but nothing beneath it.
1487 
1488                 RuleExpressionWalker.AnalyzeUsage(analysis, targetObject, true, false, null);
1489             }
1490 
1491             // See if any of the arguments need default analysis.
1492             for (int i = 0; i < invokeExpr.Parameters.Count; ++i)
1493             {
1494                 CodeExpression argExpr = invokeExpr.Parameters[i];
1495 
1496                 if (!attributedExprs.Contains(argExpr))
1497                 {
1498                     // Similar to the target object, we assume that this method can reads the value
1499                     // of the parameter, but none of its members.
1500                     RuleExpressionWalker.AnalyzeUsage(analysis, argExpr, true, false, null);
1501                 }
1502             }
1503         }
1504 
Evaluate(CodeExpression expression, RuleExecution execution)1505         internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
1506         {
1507             string message;
1508 
1509             CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
1510 
1511             object target = RuleExpressionWalker.Evaluate(execution, invokeExpr.Method.TargetObject).Value;
1512 
1513             RuleMethodInvokeExpressionInfo invokeExprInfo = execution.Validation.ExpressionInfo(invokeExpr) as RuleMethodInvokeExpressionInfo;
1514             if (invokeExprInfo == null)  // Oops, someone forgot to validate.
1515             {
1516                 message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
1517                 InvalidOperationException exception = new InvalidOperationException(message);
1518                 exception.Data[RuleUserDataKeys.ErrorObject] = invokeExpr;
1519                 throw exception;
1520             }
1521 
1522             MethodInfo mi = invokeExprInfo.MethodInfo;
1523 
1524             if (!mi.IsStatic && target == null)
1525             {
1526                 message = string.Format(CultureInfo.CurrentCulture, Messages.TargetEvaluatedNullMethod, invokeExpr.Method.MethodName);
1527                 RuleEvaluationException exception = new RuleEvaluationException(message);
1528                 exception.Data[RuleUserDataKeys.ErrorObject] = invokeExpr;
1529                 throw exception;
1530             }
1531 
1532             object[] arguments = null;
1533             RuleExpressionResult[] outArgumentResults = null;
1534 
1535             if (invokeExpr.Parameters != null && invokeExpr.Parameters.Count > 0)
1536             {
1537                 int actualArgCount = invokeExpr.Parameters.Count;
1538 
1539                 ParameterInfo[] parmInfos = mi.GetParameters();
1540 
1541                 arguments = new object[parmInfos.Length];
1542 
1543                 int numFixedParameters = parmInfos.Length;
1544                 if (invokeExprInfo.NeedsParamsExpansion)
1545                     numFixedParameters -= 1;
1546 
1547                 int i;
1548 
1549                 // Evaluate the fixed portion of the parameter list.
1550                 for (i = 0; i < numFixedParameters; ++i)
1551                 {
1552                     Type argType = execution.Validation.ExpressionInfo(invokeExpr.Parameters[i]).ExpressionType;
1553                     RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, invokeExpr.Parameters[i]);
1554 
1555                     // Special procesing of direction expressions to keep track of out arguments (& ref).
1556                     CodeDirectionExpression direction = invokeExpr.Parameters[i] as CodeDirectionExpression;
1557                     if (direction != null && (direction.Direction == FieldDirection.Ref || direction.Direction == FieldDirection.Out))
1558                     {
1559                         // lazy creation of fieldsToSet
1560                         if (outArgumentResults == null)
1561                             outArgumentResults = new RuleExpressionResult[invokeExpr.Parameters.Count];
1562                         // keep track of this out expression so we can set it later
1563                         outArgumentResults[i] = argResult;
1564 
1565                         // don't evaluate out arguments
1566                         if (direction.Direction != FieldDirection.Out)
1567                             arguments[i] = Executor.AdjustType(argType, argResult.Value, parmInfos[i].ParameterType);
1568                     }
1569                     else
1570                     {
1571                         // treat as in
1572                         arguments[i] = Executor.AdjustType(argType, argResult.Value, parmInfos[i].ParameterType);
1573                     }
1574                 }
1575 
1576                 if (numFixedParameters < actualArgCount)
1577                 {
1578                     // This target method had a params array, and we are calling it with an
1579                     // expanded parameter list.  E.g.,
1580                     //      void foo(int x, params string[] y)
1581                     // with the invocation:
1582                     //      foo(5, "crud", "kreeble", "glorp")
1583                     // We need to translate this to:
1584                     //      foo(5, new string[] { "crud", "kreeble", "glorp" })
1585 
1586                     ParameterInfo lastParamInfo = parmInfos[numFixedParameters];
1587 
1588                     Type arrayType = lastParamInfo.ParameterType;
1589                     System.Diagnostics.Debug.Assert(arrayType.IsArray);
1590                     Type elementType = arrayType.GetElementType();
1591 
1592                     Array paramsArray = (Array)arrayType.InvokeMember(arrayType.Name, BindingFlags.CreateInstance, null, null, new object[] { actualArgCount - i }, CultureInfo.CurrentCulture);
1593                     for (; i < actualArgCount; ++i)
1594                     {
1595                         Type argType = execution.Validation.ExpressionInfo(invokeExpr.Parameters[i]).ExpressionType;
1596                         RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, invokeExpr.Parameters[i]);
1597                         paramsArray.SetValue(Executor.AdjustType(argType, argResult.Value, elementType), i - numFixedParameters);
1598                     }
1599 
1600                     arguments[numFixedParameters] = paramsArray;
1601                 }
1602             }
1603 
1604             object result;
1605             try
1606             {
1607                 result = mi.Invoke(target, arguments);
1608             }
1609             catch (TargetInvocationException e)
1610             {
1611                 // if there is no inner exception, leave it untouched
1612                 if (e.InnerException == null)
1613                     throw;
1614                 message = string.Format(CultureInfo.CurrentCulture, Messages.Error_MethodInvoke,
1615                     RuleDecompiler.DecompileType(mi.ReflectedType), mi.Name, e.InnerException.Message);
1616                 throw new TargetInvocationException(message, e.InnerException);
1617             }
1618 
1619             // any out/ref parameters that need to be assigned?
1620             if (outArgumentResults != null)
1621             {
1622                 for (int i = 0; i < invokeExpr.Parameters.Count; ++i)
1623                 {
1624                     if (outArgumentResults[i] != null)
1625                         outArgumentResults[i].Value = arguments[i];
1626                 }
1627             }
1628 
1629             return new RuleLiteralResult(result);
1630         }
1631 
Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)1632         internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
1633         {
1634             CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
1635 
1636             if ((invokeExpr.Method == null) || (invokeExpr.Method.TargetObject == null))
1637             {
1638                 string message = string.Format(CultureInfo.CurrentCulture, Messages.NullMethodTarget, invokeExpr.Method.MethodName);
1639                 RuleEvaluationException exception = new RuleEvaluationException(message);
1640                 exception.Data[RuleUserDataKeys.ErrorObject] = invokeExpr;
1641                 throw exception;
1642             }
1643 
1644             // Decompile the target expression.
1645             CodeExpression targetObject = invokeExpr.Method.TargetObject;
1646             RuleExpressionWalker.Decompile(stringBuilder, targetObject, invokeExpr);
1647 
1648             stringBuilder.Append('.');
1649             stringBuilder.Append(invokeExpr.Method.MethodName);
1650 
1651             // Decompile the arguments
1652             stringBuilder.Append('(');
1653 
1654             if (invokeExpr.Parameters != null)
1655             {
1656                 for (int i = 0; i < invokeExpr.Parameters.Count; ++i)
1657                 {
1658                     CodeExpression paramExpr = invokeExpr.Parameters[i];
1659                     if (paramExpr == null)
1660                     {
1661                         string message = string.Format(CultureInfo.CurrentCulture, Messages.NullMethodTypeParameter, i.ToString(CultureInfo.CurrentCulture), invokeExpr.Method.MethodName);
1662                         RuleEvaluationException exception = new RuleEvaluationException(message);
1663                         exception.Data[RuleUserDataKeys.ErrorObject] = invokeExpr;
1664                         throw exception;
1665                     }
1666 
1667                     if (i > 0)
1668                         stringBuilder.Append(", ");
1669 
1670                     RuleExpressionWalker.Decompile(stringBuilder, paramExpr, null);
1671                 }
1672             }
1673 
1674             stringBuilder.Append(')');
1675         }
1676 
Clone(CodeExpression expression)1677         internal override CodeExpression Clone(CodeExpression expression)
1678         {
1679             CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
1680 
1681             CodeMethodInvokeExpression newMethod = new CodeMethodInvokeExpression();
1682             newMethod.Method = CloneMethodReference(invokeExpr.Method);
1683             foreach (CodeExpression argument in invokeExpr.Parameters)
1684                 newMethod.Parameters.Add(RuleExpressionWalker.Clone(argument));
1685             return newMethod;
1686         }
1687 
CloneMethodReference(CodeMethodReferenceExpression oldReference)1688         private static CodeMethodReferenceExpression CloneMethodReference(CodeMethodReferenceExpression oldReference)
1689         {
1690             CodeMethodReferenceExpression newReference = new CodeMethodReferenceExpression();
1691             newReference.MethodName = oldReference.MethodName;
1692             newReference.TargetObject = RuleExpressionWalker.Clone(oldReference.TargetObject);
1693             foreach (CodeTypeReference typeReference in oldReference.TypeArguments)
1694                 newReference.TypeArguments.Add(TypeReferenceExpression.CloneType(typeReference));
1695             ConditionHelper.CloneUserData(oldReference, newReference);
1696             return newReference;
1697         }
1698 
Match(CodeExpression expression, CodeExpression comperand)1699         internal override bool Match(CodeExpression expression, CodeExpression comperand)
1700         {
1701             CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
1702 
1703             CodeMethodInvokeExpression newMethod = (CodeMethodInvokeExpression)comperand;
1704             if (invokeExpr.Method.MethodName != newMethod.Method.MethodName)
1705                 return false;
1706             if (!RuleExpressionWalker.Match(invokeExpr.Method.TargetObject, newMethod.Method.TargetObject))
1707                 return false;
1708             if (invokeExpr.Parameters.Count != newMethod.Parameters.Count)
1709                 return false;
1710             for (int i = 0; i < invokeExpr.Parameters.Count; ++i)
1711             {
1712                 if (!RuleExpressionWalker.Match(invokeExpr.Parameters[i], newMethod.Parameters[i]))
1713                     return false;
1714             }
1715             return true;
1716         }
1717     }
1718 
1719     #endregion
1720 
1721     #region Direction expression (in/out/ref)
1722 
1723     // CodeDirectionExpression
1724     internal class DirectionExpression : RuleExpressionInternal
1725     {
Validate(CodeExpression expression, RuleValidation validation, bool isWritten)1726         internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
1727         {
1728             CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
1729 
1730             if (isWritten)
1731             {
1732                 string message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeDirectionExpression).ToString());
1733                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
1734                 error.UserData[RuleUserDataKeys.ErrorObject] = directionExpr;
1735                 validation.Errors.Add(error);
1736                 return null;
1737             }
1738 
1739             // direction specified, make sure that something is specified
1740             if (directionExpr.Expression == null)
1741             {
1742                 ValidationError error = new ValidationError(Messages.NullDirectionTarget, ErrorNumbers.Error_ParameterNotSet);
1743                 error.UserData[RuleUserDataKeys.ErrorObject] = directionExpr;
1744                 validation.Errors.Add(error);
1745                 return null;
1746             }
1747 
1748             if (directionExpr.Expression is CodeTypeReferenceExpression)
1749             {
1750                 string message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, directionExpr.Expression.GetType().FullName);
1751                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
1752                 error.UserData[RuleUserDataKeys.ErrorObject] = directionExpr.Expression;
1753                 validation.AddError(error);
1754                 return null;
1755             }
1756 
1757             // validate the parameter
1758             RuleExpressionInfo paramExprInfo;
1759             bool isRef;
1760             if (directionExpr.Direction == FieldDirection.Ref)
1761             {
1762                 // ref parameters require that we both read and write the value
1763                 isRef = true;
1764                 paramExprInfo = RuleExpressionWalker.Validate(validation, directionExpr.Expression, false);
1765                 if (paramExprInfo == null)
1766                     return null;
1767                 paramExprInfo = RuleExpressionWalker.Validate(validation, directionExpr.Expression, true);
1768             }
1769             else if (directionExpr.Direction == FieldDirection.Out)
1770             {
1771                 // out parameters mean that we only write to it
1772                 isRef = true;
1773                 paramExprInfo = RuleExpressionWalker.Validate(validation, directionExpr.Expression, true);
1774             }
1775             else
1776             {
1777                 // other parameters are treated as in, so we need to be able to read them
1778                 isRef = false;
1779                 paramExprInfo = RuleExpressionWalker.Validate(validation, directionExpr.Expression, false);
1780             }
1781             if (paramExprInfo == null)
1782                 return null;
1783 
1784             // determine it's type
1785             Type parameterType = paramExprInfo.ExpressionType;
1786             if (parameterType == null)
1787                 return null;
1788 
1789             if (parameterType != typeof(NullLiteral))
1790             {
1791                 // adjust type if necessary
1792                 if (isRef && !parameterType.IsByRef)
1793                     parameterType = parameterType.MakeByRefType();
1794             }
1795             return new RuleExpressionInfo(parameterType);
1796         }
1797 
AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)1798         internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
1799         {
1800             CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
1801             CodeExpression paramExpr = directionExpr.Expression;
1802 
1803             bool argIsWritten = false;
1804             bool argIsRead = true;
1805             RulePathQualifier argQualifier = null;
1806             switch (directionExpr.Direction)
1807             {
1808                 case FieldDirection.In:
1809                     // We assume that all children (* suffix) of this argument can be read.
1810                     argIsWritten = false;
1811                     argIsRead = true;
1812                     argQualifier = new RulePathQualifier("*", null);
1813                     break;
1814 
1815                 case FieldDirection.Ref:
1816                     // When this happens in a condition, we treat this like an "in" (above): all
1817                     // children (* suffix) of this argument are read.  When this happens in an
1818                     // action, we treat this like an "out" (below):  we assume this argument is
1819                     // modified (no suffix).
1820                     argIsWritten = true;
1821                     argIsRead = true;
1822                     argQualifier = analysis.ForWrites ? null : new RulePathQualifier("*", null);
1823                     break;
1824 
1825                 case FieldDirection.Out:
1826                     // We assume that this argument is modified (no suffix).
1827                     argIsWritten = true;
1828                     argIsRead = false;
1829                     argQualifier = null;
1830                     break;
1831             }
1832 
1833             RuleExpressionWalker.AnalyzeUsage(analysis, paramExpr, argIsRead, argIsWritten, argQualifier);
1834         }
1835 
Evaluate(CodeExpression expression, RuleExecution execution)1836         internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
1837         {
1838             // For evaluation purposes, ignore the direction.  It is handled specifically in the
1839             // method invoke Evaluate method.
1840             CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
1841             return RuleExpressionWalker.Evaluate(execution, directionExpr.Expression);
1842         }
1843 
Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)1844         internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
1845         {
1846             CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
1847 
1848             string direction = null;
1849             if (directionExpr.Direction == FieldDirection.Out)
1850                 direction = "out ";
1851             else if (directionExpr.Direction == FieldDirection.Ref)
1852                 direction = "ref ";
1853 
1854             if (direction != null)
1855                 stringBuilder.Append(direction);
1856 
1857             RuleExpressionWalker.Decompile(stringBuilder, directionExpr.Expression, directionExpr);
1858         }
1859 
Clone(CodeExpression expression)1860         internal override CodeExpression Clone(CodeExpression expression)
1861         {
1862             CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
1863             CodeDirectionExpression newDirection = new CodeDirectionExpression();
1864             newDirection.Direction = directionExpr.Direction;
1865             newDirection.Expression = RuleExpressionWalker.Clone(directionExpr.Expression);
1866             return newDirection;
1867         }
1868 
Match(CodeExpression expression, CodeExpression comperand)1869         internal override bool Match(CodeExpression expression, CodeExpression comperand)
1870         {
1871             CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
1872             CodeDirectionExpression newDirection = (CodeDirectionExpression)comperand;
1873             return (directionExpr.Direction == newDirection.Direction &&
1874                 RuleExpressionWalker.Match(directionExpr.Expression, newDirection.Expression));
1875         }
1876     }
1877 
1878     #endregion
1879 
1880     #region Type Reference expression
1881 
1882     // CodeTypeReferenceExpression
1883     internal class TypeReferenceExpression : RuleExpressionInternal
1884     {
Validate(CodeExpression expression, RuleValidation validation, bool isWritten)1885         internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
1886         {
1887             CodeTypeReferenceExpression typeRefExpr = (CodeTypeReferenceExpression)expression;
1888 
1889             if (typeRefExpr.Type == null)
1890             {
1891                 ValidationError error = new ValidationError(Messages.NullTypeType, ErrorNumbers.Error_ParameterNotSet);
1892                 error.UserData[RuleUserDataKeys.ErrorObject] = typeRefExpr;
1893                 validation.Errors.Add(error);
1894                 return null;
1895             }
1896 
1897             if (isWritten)
1898             {
1899                 string message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeTypeReferenceExpression).ToString());
1900                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
1901                 error.UserData[RuleUserDataKeys.ErrorObject] = typeRefExpr;
1902                 validation.Errors.Add(error);
1903                 return null;
1904             }
1905 
1906             Type resultType = validation.ResolveType(typeRefExpr.Type);
1907 
1908             return new RuleExpressionInfo(resultType);
1909         }
1910 
AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)1911         internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
1912         {
1913             // These introduce no interesting dependencies or side-effects.
1914         }
1915 
Evaluate(CodeExpression expression, RuleExecution execution)1916         internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
1917         {
1918             // Type references don't evaluate to any value.
1919             return new RuleLiteralResult(null);
1920         }
1921 
Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)1922         internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
1923         {
1924             CodeTypeReferenceExpression typeRefExpr = (CodeTypeReferenceExpression)expression;
1925             RuleDecompiler.DecompileType(stringBuilder, typeRefExpr.Type);
1926         }
1927 
Clone(CodeExpression expression)1928         internal override CodeExpression Clone(CodeExpression expression)
1929         {
1930             CodeTypeReferenceExpression typeRefExpr = (CodeTypeReferenceExpression)expression;
1931             CodeTypeReferenceExpression newType = new CodeTypeReferenceExpression(CloneType(typeRefExpr.Type));
1932             return newType;
1933         }
1934 
CloneType(CodeTypeReference oldType)1935         static internal CodeTypeReference CloneType(CodeTypeReference oldType)
1936         {
1937             if (oldType == null)
1938                 return null;
1939 
1940             CodeTypeReference newType = new CodeTypeReference();
1941             newType.ArrayElementType = CloneType(oldType.ArrayElementType);
1942             newType.ArrayRank = oldType.ArrayRank;
1943             newType.BaseType = oldType.BaseType;
1944             foreach (CodeTypeReference typeReference in oldType.TypeArguments)
1945                 newType.TypeArguments.Add(CloneType(typeReference));
1946 
1947             ConditionHelper.CloneUserData(oldType, newType);
1948 
1949             return newType;
1950         }
1951 
Match(CodeExpression expression, CodeExpression comperand)1952         internal override bool Match(CodeExpression expression, CodeExpression comperand)
1953         {
1954             CodeTypeReferenceExpression typeRefExpr = (CodeTypeReferenceExpression)expression;
1955             CodeTypeReferenceExpression newType = (CodeTypeReferenceExpression)comperand;
1956             return MatchType(typeRefExpr.Type, newType.Type);
1957         }
1958 
MatchType(CodeTypeReference typeRef1, CodeTypeReference typeRef2)1959         static internal bool MatchType(CodeTypeReference typeRef1, CodeTypeReference typeRef2)
1960         {
1961             if (typeRef1.BaseType != typeRef2.BaseType)
1962                 return false;
1963 
1964             if (typeRef1.TypeArguments.Count != typeRef2.TypeArguments.Count)
1965                 return false;
1966             for (int i = 0; i < typeRef1.TypeArguments.Count; ++i)
1967             {
1968                 CodeTypeReference trArg1 = typeRef1.TypeArguments[i];
1969                 CodeTypeReference trArg2 = typeRef2.TypeArguments[i];
1970 
1971                 if (!MatchType(trArg1, trArg2))
1972                     return false;
1973             }
1974 
1975             return true;
1976         }
1977     }
1978 
1979     #endregion
1980 
1981     #region Cast expression
1982 
1983     // CodeCastExpression
1984     internal class CastExpression : RuleExpressionInternal
1985     {
1986         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
Validate(CodeExpression expression, RuleValidation validation, bool isWritten)1987         internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
1988         {
1989             string message;
1990 
1991             CodeCastExpression castExpr = (CodeCastExpression)expression;
1992 
1993             if (isWritten)
1994             {
1995                 message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeCastExpression).ToString());
1996                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
1997                 error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
1998                 validation.Errors.Add(error);
1999                 return null;
2000             }
2001 
2002             if (castExpr.Expression == null)
2003             {
2004                 ValidationError error = new ValidationError(Messages.NullCastExpr, ErrorNumbers.Error_ParameterNotSet);
2005                 error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
2006                 validation.Errors.Add(error);
2007                 return null;
2008             }
2009 
2010             if (castExpr.Expression is CodeTypeReferenceExpression)
2011             {
2012                 message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, castExpr.Expression.GetType().FullName);
2013                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
2014                 error.UserData[RuleUserDataKeys.ErrorObject] = castExpr.Expression;
2015                 validation.AddError(error);
2016                 return null;
2017             }
2018 
2019             if (castExpr.TargetType == null)
2020             {
2021                 ValidationError error = new ValidationError(Messages.NullCastType, ErrorNumbers.Error_ParameterNotSet);
2022                 error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
2023                 validation.Errors.Add(error);
2024                 return null;
2025             }
2026 
2027             // Figure out the operand type.
2028             RuleExpressionInfo operandInfo = RuleExpressionWalker.Validate(validation, castExpr.Expression, false);
2029             if (operandInfo == null)
2030                 return null;
2031             Type fromType = operandInfo.ExpressionType;
2032 
2033             Type toType = validation.ResolveType(castExpr.TargetType);
2034             if (toType == null)
2035                 return null;
2036 
2037             if (fromType == typeof(NullLiteral))
2038             {
2039                 // Casting from null value.
2040                 if (ConditionHelper.IsNonNullableValueType(toType))
2041                 {
2042                     message = string.Format(CultureInfo.CurrentCulture, Messages.CastOfNullInvalid, RuleDecompiler.DecompileType(toType));
2043                     ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
2044                     error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
2045                     validation.Errors.Add(error);
2046                     return null;
2047                 }
2048             }
2049             else
2050             {
2051                 // Unwrap nullables to make life easy.
2052                 Type fromType2 = fromType;
2053                 if (ConditionHelper.IsNullableValueType(fromType2))
2054                     fromType2 = fromType2.GetGenericArguments()[0];
2055 
2056                 Type toType2 = toType;
2057                 if (ConditionHelper.IsNullableValueType(toType2))
2058                     toType2 = toType2.GetGenericArguments()[0];
2059 
2060                 bool canConvert = false;
2061                 if (fromType2.IsValueType && toType2.IsValueType)
2062                 {
2063                     // Convert.ChangeType doesn't handle enum <--> numeric
2064                     // and float/double/decimal <--> char, which are allowed
2065                     if (fromType2.IsEnum)
2066                     {
2067                         canConvert = (toType2.IsEnum) || IsNumeric(toType2);
2068                     }
2069                     else if (toType2.IsEnum)
2070                     {
2071                         // don't need to check fromType for enum since it's handled above
2072                         canConvert = IsNumeric(fromType2);
2073                     }
2074                     else if (fromType2 == typeof(char))
2075                     {
2076                         canConvert = IsNumeric(toType2);
2077                     }
2078                     else if (toType2 == typeof(char))
2079                     {
2080                         canConvert = IsNumeric(fromType2);
2081                     }
2082                     else if (fromType2.IsPrimitive && toType2.IsPrimitive)
2083                     {
2084                         try
2085                         {
2086                             // note: this also allows bool <--> numeric conversions
2087                             object fromValueDefault = Activator.CreateInstance(fromType2);
2088                             Convert.ChangeType(fromValueDefault, toType2, CultureInfo.CurrentCulture);
2089                             canConvert = true;
2090                         }
2091                         catch (Exception)
2092                         {
2093                             canConvert = false;
2094                         }
2095                     }
2096                 }
2097 
2098                 if (!canConvert)
2099                 {
2100                     // We can cast up or down an inheritence hierarchy,
2101                     // as well as support explicit and implicit overrides
2102                     ValidationError error;
2103                     canConvert = RuleValidation.ExplicitConversionSpecified(fromType, toType, out error);
2104                     if (error != null)
2105                     {
2106                         error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
2107                         validation.Errors.Add(error);
2108                         return null;
2109                     }
2110                 }
2111 
2112                 if (!canConvert)
2113                 {
2114                     message = string.Format(CultureInfo.CurrentCulture, Messages.CastIncompatibleTypes, RuleDecompiler.DecompileType(fromType), RuleDecompiler.DecompileType(toType));
2115                     ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
2116                     error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
2117                     validation.Errors.Add(error);
2118                     return null;
2119                 }
2120             }
2121 
2122             return new RuleExpressionInfo(toType);
2123         }
2124 
IsNumeric(Type type)2125         private static bool IsNumeric(Type type)
2126         {
2127             switch (Type.GetTypeCode(type))
2128             {
2129                 case TypeCode.SByte:
2130                 case TypeCode.Byte:
2131                 case TypeCode.Int16:
2132                 case TypeCode.UInt16:
2133                 case TypeCode.Int32:
2134                 case TypeCode.UInt32:
2135                 case TypeCode.Int64:
2136                 case TypeCode.UInt64:
2137                 case TypeCode.Char:
2138                 case TypeCode.Single:
2139                 case TypeCode.Double:
2140                 case TypeCode.Decimal:
2141                     return true;
2142                 default:
2143                     return false;
2144             }
2145         }
2146 
AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)2147         internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
2148         {
2149             // Just analyze the child.
2150             CodeCastExpression castExpr = (CodeCastExpression)expression;
2151             RuleExpressionWalker.AnalyzeUsage(analysis, castExpr.Expression, true, false, null);
2152         }
2153 
Evaluate(CodeExpression expression, RuleExecution execution)2154         internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
2155         {
2156             string message;
2157 
2158             CodeCastExpression castExpr = (CodeCastExpression)expression;
2159 
2160             // Evaluate the operand.
2161             object operandValue = RuleExpressionWalker.Evaluate(execution, castExpr.Expression).Value;
2162 
2163             // Get the cast-to type.
2164             RuleExpressionInfo castExprInfo = execution.Validation.ExpressionInfo(castExpr);
2165             if (castExprInfo == null)  // Oops, someone forgot to validate.
2166             {
2167                 message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
2168                 InvalidOperationException exception = new InvalidOperationException(message);
2169                 exception.Data[RuleUserDataKeys.ErrorObject] = castExpr;
2170                 throw exception;
2171             }
2172             Type toType = castExprInfo.ExpressionType;
2173 
2174             // Handle null operand result.
2175             if (operandValue == null)
2176             {
2177                 // Here we are casting null to something. If it is a value type we can't do it.
2178                 if (ConditionHelper.IsNonNullableValueType(toType))
2179                 {
2180                     message = string.Format(CultureInfo.CurrentCulture, Messages.CastIncompatibleTypes, Messages.NullValue, RuleDecompiler.DecompileType(toType));
2181                     RuleEvaluationException exception = new RuleEvaluationException(message);
2182                     exception.Data[RuleUserDataKeys.ErrorObject] = castExpr;
2183                     throw exception;
2184                 }
2185                 // If it's not a value type, null is good.
2186             }
2187             else
2188             {
2189                 Type operandType = execution.Validation.ExpressionInfo(castExpr.Expression).ExpressionType;
2190                 operandValue = Executor.AdjustTypeWithCast(operandType, operandValue, toType);
2191             }
2192 
2193             return new RuleLiteralResult(operandValue);
2194         }
2195 
Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)2196         internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
2197         {
2198             CodeCastExpression castExpr = (CodeCastExpression)expression;
2199 
2200             CodeExpression targetObject = castExpr.Expression;
2201             if (targetObject == null)
2202             {
2203                 RuleEvaluationException exception = new RuleEvaluationException(Messages.NullCastExpr);
2204                 exception.Data[RuleUserDataKeys.ErrorObject] = castExpr;
2205                 throw exception;
2206             }
2207 
2208             if (castExpr.TargetType == null)
2209             {
2210                 RuleEvaluationException exception = new RuleEvaluationException(Messages.NullCastType);
2211                 exception.Data[RuleUserDataKeys.ErrorObject] = castExpr;
2212                 throw exception;
2213             }
2214 
2215             bool mustParenthesize = RuleDecompiler.MustParenthesize(castExpr, parentExpression);
2216             if (mustParenthesize)
2217                 stringBuilder.Append("(");
2218 
2219             stringBuilder.Append("(");
2220             RuleDecompiler.DecompileType(stringBuilder, castExpr.TargetType);
2221             stringBuilder.Append(")");
2222             RuleExpressionWalker.Decompile(stringBuilder, targetObject, castExpr);
2223 
2224             if (mustParenthesize)
2225                 stringBuilder.Append(")");
2226         }
2227 
Clone(CodeExpression expression)2228         internal override CodeExpression Clone(CodeExpression expression)
2229         {
2230             CodeCastExpression castExpr = (CodeCastExpression)expression;
2231             CodeCastExpression newCast = new CodeCastExpression();
2232             newCast.TargetType = TypeReferenceExpression.CloneType(castExpr.TargetType);
2233             newCast.Expression = RuleExpressionWalker.Clone(castExpr.Expression);
2234             return newCast;
2235         }
2236 
Match(CodeExpression expression, CodeExpression comperand)2237         internal override bool Match(CodeExpression expression, CodeExpression comperand)
2238         {
2239             CodeCastExpression castExpr = (CodeCastExpression)expression;
2240             CodeCastExpression castComperand = (CodeCastExpression)comperand;
2241 
2242             return TypeReferenceExpression.MatchType(castExpr.TargetType, castComperand.TargetType) &&
2243                 RuleExpressionWalker.Match(castExpr.Expression, castComperand.Expression);
2244         }
2245     }
2246 
2247     #endregion
2248 
2249     #region Indexer Expression (indexer properties)
2250 
2251     // CodeIndexerExpression
2252     internal class IndexerPropertyExpression : RuleExpressionInternal
2253     {
2254         [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
Validate(CodeExpression expression, RuleValidation validation, bool isWritten)2255         internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
2256         {
2257             string message;
2258             ValidationError error = null;
2259             RulePropertyExpressionInfo propExprInfo = null;
2260             bool includeNonPublic = false;
2261             Type targetType = null;
2262 
2263             CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
2264 
2265             CodeExpression targetObject = indexerExpr.TargetObject;
2266             if (targetObject == null)
2267             {
2268                 error = new ValidationError(Messages.NullIndexerTarget, ErrorNumbers.Error_ParameterNotSet);
2269                 error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
2270                 validation.Errors.Add(error);
2271                 return null;
2272             }
2273 
2274             if (targetObject is CodeTypeReferenceExpression)
2275             {
2276                 error = new ValidationError(Messages.IndexersCannotBeStatic, ErrorNumbers.Error_ParameterNotSet);
2277                 error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
2278                 validation.Errors.Add(error);
2279                 return null;
2280             }
2281 
2282             if (indexerExpr.Indices == null || indexerExpr.Indices.Count == 0)
2283             {
2284                 error = new ValidationError(Messages.MissingIndexExpressions, ErrorNumbers.Error_ParameterNotSet);
2285                 error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
2286                 validation.Errors.Add(error);
2287                 return null;
2288             }
2289 
2290             try
2291             {
2292                 // Early exit from this if a cycle is detected.
2293                 if (!validation.PushParentExpression(indexerExpr))
2294                     return null;
2295 
2296                 RuleExpressionInfo targetExprInfo = RuleExpressionWalker.Validate(validation, indexerExpr.TargetObject, false);
2297                 if (targetExprInfo == null)     // error occurred, so simply return
2298                     return null;
2299 
2300                 targetType = targetExprInfo.ExpressionType;
2301                 if (targetType == null)
2302                     return null;
2303 
2304                 // if an error occurred (targetType == null), continue on to validate the arguments
2305                 if (targetType == typeof(NullLiteral))
2306                 {
2307                     message = string.Format(CultureInfo.CurrentCulture, Messages.NullIndexerTarget);
2308                     error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
2309                     error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
2310                     validation.Errors.Add(error);
2311                     targetType = null; // force exit after validating the arguments
2312                 }
2313 
2314                 List<CodeExpression> argExprs = new List<CodeExpression>();
2315 
2316                 bool hasInvalidArgument = false;
2317                 for (int i = 0; i < indexerExpr.Indices.Count; ++i)
2318                 {
2319                     CodeExpression argExpr = indexerExpr.Indices[i];
2320                     if (argExpr == null)
2321                     {
2322                         error = new ValidationError(Messages.NullIndexExpression, ErrorNumbers.Error_ParameterNotSet);
2323                         error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
2324                         validation.Errors.Add(error);
2325                         hasInvalidArgument = true;
2326                     }
2327                     else
2328                     {
2329                         CodeDirectionExpression argDirection = argExpr as CodeDirectionExpression;
2330                         if (argDirection != null && argDirection.Direction != FieldDirection.In)
2331                         {
2332                             // No "ref" or "out" arguments are allowed on indexer arguments.
2333                             error = new ValidationError(Messages.IndexerArgCannotBeRefOrOut, ErrorNumbers.Error_IndexerArgCannotBeRefOrOut);
2334                             error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
2335                             validation.Errors.Add(error);
2336                             hasInvalidArgument = true;
2337                         }
2338 
2339                         if (argExpr is CodeTypeReferenceExpression)
2340                         {
2341                             message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, argExpr.GetType().FullName);
2342                             error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
2343                             error.UserData[RuleUserDataKeys.ErrorObject] = argExpr;
2344                             validation.AddError(error);
2345                             hasInvalidArgument = true;
2346                         }
2347 
2348                         // Validate the argument.
2349                         RuleExpressionInfo argExprInfo = RuleExpressionWalker.Validate(validation, argExpr, false);
2350                         if (argExprInfo == null)
2351                             hasInvalidArgument = true;
2352                         else
2353                             argExprs.Add(argExpr);
2354                     }
2355                 }
2356 
2357                 // Stop further validation if there was a problem with the target expression.
2358                 if (targetType == null)
2359                     return null;
2360 
2361                 // Stop further validation if there was a problem with any of the arguments.
2362                 if (hasInvalidArgument)
2363                     return null;
2364 
2365                 BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;
2366                 if (validation.AllowInternalMembers(targetType))
2367                 {
2368                     bindingFlags |= BindingFlags.NonPublic;
2369                     includeNonPublic = true;
2370                 }
2371 
2372                 // Everything okay so far, try to resolve the method.
2373                 propExprInfo = validation.ResolveIndexerProperty(targetType, bindingFlags, argExprs, out error);
2374                 if (propExprInfo == null)
2375                 {
2376                     error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
2377                     validation.Errors.Add(error);
2378                     return null;
2379                 }
2380             }
2381             finally
2382             {
2383                 validation.PopParentExpression();
2384             }
2385 
2386             PropertyInfo pi = propExprInfo.PropertyInfo;
2387 
2388             MethodInfo accessorMethod = isWritten ? pi.GetSetMethod(includeNonPublic) : pi.GetGetMethod(includeNonPublic);
2389             if (accessorMethod == null)
2390             {
2391                 string baseMessage = isWritten ? Messages.UnknownPropertySet : Messages.UnknownPropertyGet;
2392                 message = string.Format(CultureInfo.CurrentCulture, baseMessage, pi.Name, RuleDecompiler.DecompileType(targetType));
2393                 error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
2394                 error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
2395                 validation.Errors.Add(error);
2396                 return null;
2397             }
2398 
2399             if (!validation.ValidateMemberAccess(targetObject, targetType, accessorMethod, pi.Name, indexerExpr))
2400                 return null;
2401 
2402             // Validate any RuleAttributes, if present.
2403             object[] attrs = pi.GetCustomAttributes(typeof(RuleAttribute), true);
2404             if (attrs != null && attrs.Length > 0)
2405             {
2406                 Stack<MemberInfo> methodStack = new Stack<MemberInfo>();
2407                 methodStack.Push(pi);
2408 
2409                 bool allAttributesValid = true;
2410                 foreach (RuleAttribute ruleAttr in attrs)
2411                 {
2412                     if (!ruleAttr.Validate(validation, pi, targetType, pi.GetIndexParameters()))
2413                         allAttributesValid = false;
2414                 }
2415 
2416                 methodStack.Pop();
2417 
2418                 if (!allAttributesValid)
2419                     return null;
2420             }
2421 
2422             return propExprInfo;
2423         }
2424 
2425 
AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)2426         internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
2427         {
2428             string message;
2429 
2430             CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
2431 
2432             // Evaluate the target object and get its type.
2433             CodeExpression targetObject = indexerExpr.TargetObject;
2434             RuleExpressionInfo targetExprInfo = analysis.Validation.ExpressionInfo(targetObject);
2435             if (targetExprInfo == null)  // Oops, someone forgot to validate.
2436             {
2437                 message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
2438                 InvalidOperationException exception = new InvalidOperationException(message);
2439                 exception.Data[RuleUserDataKeys.ErrorObject] = targetObject;
2440                 throw exception;
2441             }
2442 
2443             // Get the property info from the validator so we can look for [RuleRead] and [RuleWrite] attributes.
2444             RulePropertyExpressionInfo propExprInfo = analysis.Validation.ExpressionInfo(indexerExpr) as RulePropertyExpressionInfo;
2445             if (propExprInfo == null)  // Oops, someone forgot to validate.
2446             {
2447                 message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
2448                 InvalidOperationException exception = new InvalidOperationException(message);
2449                 exception.Data[RuleUserDataKeys.ErrorObject] = indexerExpr;
2450                 throw exception;
2451             }
2452 
2453             PropertyInfo pi = propExprInfo.PropertyInfo;
2454 
2455             // Look for RuleAttribute's on the invoked indexer property.
2456             List<CodeExpression> attributedExprs = new List<CodeExpression>();
2457             analysis.AnalyzeRuleAttributes(pi, targetObject, qualifier, indexerExpr.Indices, pi.GetIndexParameters(), attributedExprs);
2458 
2459             // See if the target object needs default analysis.
2460             if (!attributedExprs.Contains(targetObject))
2461             {
2462                 // The property had no [RuleRead] or [RuleWrite] attributes.  The target object is read or
2463                 // written (as the case may be).
2464 
2465                 RuleExpressionWalker.AnalyzeUsage(analysis, targetObject, isRead, isWritten, qualifier);
2466             }
2467 
2468             // See if any of the arguments need default analysis.
2469             for (int i = 0; i < indexerExpr.Indices.Count; ++i)
2470             {
2471                 CodeExpression argExpr = indexerExpr.Indices[i];
2472 
2473                 if (!attributedExprs.Contains(argExpr))
2474                 {
2475                     // Similar to the target object, we assume that this method can reads the value
2476                     // of the parameter, but none of its members.
2477                     RuleExpressionWalker.AnalyzeUsage(analysis, argExpr, true, false, null);
2478                 }
2479             }
2480         }
2481 
Evaluate(CodeExpression expression, RuleExecution execution)2482         internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
2483         {
2484             string message;
2485 
2486             CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
2487 
2488             RulePropertyExpressionInfo propExprInfo = execution.Validation.ExpressionInfo(indexerExpr) as RulePropertyExpressionInfo;
2489             if (propExprInfo == null)  // Oops, someone forgot to validate.
2490             {
2491                 message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
2492                 InvalidOperationException exception = new InvalidOperationException(message);
2493                 exception.Data[RuleUserDataKeys.ErrorObject] = indexerExpr;
2494                 throw exception;
2495             }
2496 
2497             PropertyInfo pi = propExprInfo.PropertyInfo;
2498 
2499             // Evaluate the target...
2500             object target = RuleExpressionWalker.Evaluate(execution, indexerExpr.TargetObject).Value;
2501 
2502             if (target == null)
2503             {
2504                 message = string.Format(CultureInfo.CurrentCulture, Messages.TargetEvaluatedNullIndexer);
2505                 RuleEvaluationException exception = new RuleEvaluationException(message);
2506                 exception.Data[RuleUserDataKeys.ErrorObject] = indexerExpr;
2507                 throw exception;
2508             }
2509 
2510             // Evaluate the index arguments.
2511             int actualArgCount = indexerExpr.Indices.Count;
2512             ParameterInfo[] parmInfos = pi.GetIndexParameters();
2513             object[] indexArgs = new object[parmInfos.Length];
2514 
2515             int numFixedParameters = parmInfos.Length;
2516             if (propExprInfo.NeedsParamsExpansion)
2517                 numFixedParameters -= 1;
2518 
2519             int i;
2520             for (i = 0; i < numFixedParameters; ++i)
2521             {
2522                 Type argType = execution.Validation.ExpressionInfo(indexerExpr.Indices[i]).ExpressionType;
2523                 RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, indexerExpr.Indices[i]);
2524                 indexArgs[i] = Executor.AdjustType(argType, argResult.Value, parmInfos[i].ParameterType);
2525             }
2526 
2527             if (numFixedParameters < actualArgCount)
2528             {
2529                 // This target indexer had a params array, and we are calling it with an
2530                 // expanded parameter list.  E.g.,
2531                 //      int this[int x, params string[] y]
2532                 // with the invocation:
2533                 //      x.y[5, "crud", "kreeble", "glorp"]
2534                 // We need to translate this to:
2535                 //      x.y[5, new string[] { "crud", "kreeble", "glorp" }]
2536 
2537                 ParameterInfo lastParamInfo = parmInfos[numFixedParameters];
2538 
2539                 Type arrayType = lastParamInfo.ParameterType;
2540                 System.Diagnostics.Debug.Assert(arrayType.IsArray);
2541                 Type elementType = arrayType.GetElementType();
2542 
2543                 Array paramsArray = (Array)arrayType.InvokeMember(arrayType.Name, BindingFlags.CreateInstance, null, null, new object[] { actualArgCount - i }, CultureInfo.CurrentCulture);
2544                 for (; i < actualArgCount; ++i)
2545                 {
2546                     Type argType = execution.Validation.ExpressionInfo(indexerExpr.Indices[i]).ExpressionType;
2547                     RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, indexerExpr.Indices[i]);
2548                     paramsArray.SetValue(Executor.AdjustType(argType, argResult.Value, elementType), i - numFixedParameters);
2549                 }
2550 
2551                 indexArgs[numFixedParameters] = paramsArray;
2552             }
2553 
2554             RulePropertyResult result = new RulePropertyResult(pi, target, indexArgs);
2555             return result;
2556         }
2557 
Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)2558         internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
2559         {
2560             string message;
2561 
2562             CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
2563 
2564             CodeExpression targetObject = indexerExpr.TargetObject;
2565             if (targetObject == null)
2566             {
2567                 message = string.Format(CultureInfo.CurrentCulture, Messages.NullIndexerTarget);
2568                 RuleEvaluationException exception = new RuleEvaluationException(message);
2569                 exception.Data[RuleUserDataKeys.ErrorObject] = indexerExpr;
2570                 throw exception;
2571             }
2572 
2573             if (indexerExpr.Indices == null || indexerExpr.Indices.Count == 0)
2574             {
2575                 message = string.Format(CultureInfo.CurrentCulture, Messages.MissingIndexExpressions);
2576                 RuleEvaluationException exception = new RuleEvaluationException(message);
2577                 exception.Data[RuleUserDataKeys.ErrorObject] = indexerExpr;
2578                 throw exception;
2579             }
2580 
2581             RuleExpressionWalker.Decompile(stringBuilder, targetObject, indexerExpr);
2582             stringBuilder.Append('[');
2583             RuleExpressionWalker.Decompile(stringBuilder, indexerExpr.Indices[0], null);
2584             for (int i = 1; i < indexerExpr.Indices.Count; ++i)
2585             {
2586                 stringBuilder.Append(", ");
2587                 RuleExpressionWalker.Decompile(stringBuilder, indexerExpr.Indices[i], null);
2588             }
2589             stringBuilder.Append(']');
2590         }
2591 
Clone(CodeExpression expression)2592         internal override CodeExpression Clone(CodeExpression expression)
2593         {
2594             CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
2595 
2596             CodeExpression targetObject = RuleExpressionWalker.Clone(indexerExpr.TargetObject);
2597 
2598             CodeExpression[] indices = new CodeExpression[indexerExpr.Indices.Count];
2599             for (int i = 0; i < indices.Length; ++i)
2600                 indices[i] = RuleExpressionWalker.Clone(indexerExpr.Indices[i]);
2601 
2602             CodeIndexerExpression newIndexer = new CodeIndexerExpression(targetObject, indices);
2603             return newIndexer;
2604         }
2605 
Match(CodeExpression expression, CodeExpression comperand)2606         internal override bool Match(CodeExpression expression, CodeExpression comperand)
2607         {
2608             CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
2609 
2610             CodeIndexerExpression indexerComperand = (CodeIndexerExpression)comperand;
2611             if (!RuleExpressionWalker.Match(indexerExpr.TargetObject, indexerComperand.TargetObject))
2612                 return false;
2613 
2614             if (indexerExpr.Indices.Count != indexerComperand.Indices.Count)
2615                 return false;
2616 
2617             for (int i = 0; i < indexerExpr.Indices.Count; ++i)
2618             {
2619                 if (!RuleExpressionWalker.Match(indexerExpr.Indices[i], indexerComperand.Indices[i]))
2620                     return false;
2621             }
2622 
2623             return true;
2624         }
2625     }
2626 
2627     #endregion
2628 
2629     #region Array Indexer Expression (indexer properties)
2630 
2631     // CodeArrayIndexerExpression
2632     internal class ArrayIndexerExpression : RuleExpressionInternal
2633     {
Validate(CodeExpression expression, RuleValidation validation, bool isWritten)2634         internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
2635         {
2636             string message;
2637             ValidationError error = null;
2638             Type targetType = null;
2639 
2640             CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
2641 
2642             CodeExpression targetObject = arrayIndexerExpr.TargetObject;
2643             if (targetObject == null)
2644             {
2645                 error = new ValidationError(Messages.NullIndexerTarget, ErrorNumbers.Error_ParameterNotSet);
2646                 error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
2647                 validation.Errors.Add(error);
2648                 return null;
2649             }
2650 
2651             if (targetObject is CodeTypeReferenceExpression)
2652             {
2653                 error = new ValidationError(Messages.IndexersCannotBeStatic, ErrorNumbers.Error_ParameterNotSet);
2654                 error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
2655                 validation.Errors.Add(error);
2656                 return null;
2657             }
2658 
2659             if (arrayIndexerExpr.Indices == null || arrayIndexerExpr.Indices.Count == 0)
2660             {
2661                 error = new ValidationError(Messages.MissingIndexExpressions, ErrorNumbers.Error_ParameterNotSet);
2662                 error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
2663                 validation.Errors.Add(error);
2664                 return null;
2665             }
2666 
2667             try
2668             {
2669                 // Early exit from this if a cycle is detected.
2670                 if (!validation.PushParentExpression(arrayIndexerExpr))
2671                     return null;
2672 
2673                 RuleExpressionInfo targetExprInfo = RuleExpressionWalker.Validate(validation, arrayIndexerExpr.TargetObject, false);
2674                 if (targetExprInfo == null)     // error occurred, so simply return
2675                     return null;
2676 
2677                 targetType = targetExprInfo.ExpressionType;
2678                 if (targetType == null)
2679                     return null;
2680 
2681                 // if an error occurred (targetType == null), continue on to validate the arguments
2682                 if (targetType == typeof(NullLiteral))
2683                 {
2684                     error = new ValidationError(Messages.NullIndexerTarget, ErrorNumbers.Error_ParameterNotSet);
2685                     error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
2686                     validation.Errors.Add(error);
2687                     return null;
2688                 }
2689 
2690                 // The target type better be an array.
2691                 if (!targetType.IsArray)
2692                 {
2693                     message = string.Format(CultureInfo.CurrentCulture, Messages.CannotIndexType, RuleDecompiler.DecompileType(targetType));
2694                     error = new ValidationError(message, ErrorNumbers.Error_CannotIndexType);
2695                     error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
2696                     validation.Errors.Add(error);
2697                     return null;
2698                 }
2699 
2700                 int rank = targetType.GetArrayRank();
2701                 if (arrayIndexerExpr.Indices.Count != rank)
2702                 {
2703                     message = string.Format(CultureInfo.CurrentCulture, Messages.ArrayIndexBadRank, rank);
2704                     error = new ValidationError(message, ErrorNumbers.Error_ArrayIndexBadRank);
2705                     error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
2706                     validation.Errors.Add(error);
2707                     return null;
2708                 }
2709 
2710                 bool hasInvalidArgument = false;
2711                 for (int i = 0; i < arrayIndexerExpr.Indices.Count; ++i)
2712                 {
2713                     CodeExpression argExpr = arrayIndexerExpr.Indices[i];
2714                     if (argExpr == null)
2715                     {
2716                         error = new ValidationError(Messages.NullIndexExpression, ErrorNumbers.Error_ParameterNotSet);
2717                         error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
2718                         validation.Errors.Add(error);
2719                         hasInvalidArgument = true;
2720                     }
2721                     else
2722                     {
2723                         CodeDirectionExpression argDirection = argExpr as CodeDirectionExpression;
2724                         if (argDirection != null)
2725                         {
2726                             // No "ref" or "out" arguments are allowed on indexer arguments.
2727                             error = new ValidationError(Messages.IndexerArgCannotBeRefOrOut, ErrorNumbers.Error_IndexerArgCannotBeRefOrOut);
2728                             error.UserData[RuleUserDataKeys.ErrorObject] = argExpr;
2729                             validation.Errors.Add(error);
2730                             hasInvalidArgument = true;
2731                         }
2732 
2733                         if (argExpr is CodeTypeReferenceExpression)
2734                         {
2735                             message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, argExpr.GetType().FullName);
2736                             error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
2737                             error.UserData[RuleUserDataKeys.ErrorObject] = argExpr;
2738                             validation.AddError(error);
2739                             hasInvalidArgument = true;
2740                         }
2741 
2742                         // Validate the argument.
2743                         RuleExpressionInfo argExprInfo = RuleExpressionWalker.Validate(validation, argExpr, false);
2744                         if (argExprInfo != null)
2745                         {
2746                             Type argType = argExprInfo.ExpressionType;
2747                             TypeCode argTypeCode = Type.GetTypeCode(argType);
2748 
2749                             // Any type that is, or can be converted to: int or long.
2750                             switch (argTypeCode)
2751                             {
2752                                 case TypeCode.Byte:
2753                                 case TypeCode.Char:
2754                                 case TypeCode.Int16:
2755                                 case TypeCode.Int32:
2756                                 case TypeCode.Int64:
2757                                 case TypeCode.SByte:
2758                                 case TypeCode.UInt16:
2759                                     break;
2760 
2761                                 default:
2762                                     message = string.Format(CultureInfo.CurrentCulture, Messages.ArrayIndexBadType, RuleDecompiler.DecompileType(argType));
2763                                     error = new ValidationError(message, ErrorNumbers.Error_ArrayIndexBadType);
2764                                     error.UserData[RuleUserDataKeys.ErrorObject] = argExpr;
2765                                     validation.Errors.Add(error);
2766                                     hasInvalidArgument = true;
2767                                     break;
2768                             }
2769                         }
2770                         else
2771                         {
2772                             hasInvalidArgument = true;
2773                         }
2774                     }
2775                 }
2776 
2777                 // Stop further validation if there was a problem with any of the arguments.
2778                 if (hasInvalidArgument)
2779                     return null;
2780             }
2781             finally
2782             {
2783                 validation.PopParentExpression();
2784             }
2785 
2786             // The result type is this array's element type.
2787             return new RuleExpressionInfo(targetType.GetElementType());
2788         }
2789 
2790 
AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)2791         internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
2792         {
2793             // Analyze the target object, flowing down the qualifier from above.  An expression
2794             // like:
2795             //      this.a.b[2,3].c[4].d[5] = 99;
2796             // should produce a path similar to:
2797             //      this/a/b/c/d
2798 
2799             CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
2800             RuleExpressionWalker.AnalyzeUsage(analysis, arrayIndexerExpr.TargetObject, isRead, isWritten, qualifier);
2801 
2802             // Analyze the indexer arguments.  They are read.
2803             for (int i = 0; i < arrayIndexerExpr.Indices.Count; ++i)
2804                 RuleExpressionWalker.AnalyzeUsage(analysis, arrayIndexerExpr.Indices[i], true, false, null);
2805         }
2806 
Evaluate(CodeExpression expression, RuleExecution execution)2807         internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
2808         {
2809             CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
2810 
2811             // Evaluate the target...
2812             object target = RuleExpressionWalker.Evaluate(execution, arrayIndexerExpr.TargetObject).Value;
2813 
2814             if (target == null)
2815             {
2816                 string message = string.Format(CultureInfo.CurrentCulture, Messages.TargetEvaluatedNullIndexer);
2817                 RuleEvaluationException exception = new RuleEvaluationException(message);
2818                 exception.Data[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
2819                 throw exception;
2820             }
2821 
2822             // Evaluate the index arguments (converting them to "longs")
2823             int actualArgCount = arrayIndexerExpr.Indices.Count;
2824             long[] indexArgs = new long[actualArgCount];
2825 
2826             for (int i = 0; i < actualArgCount; ++i)
2827             {
2828                 Type argType = execution.Validation.ExpressionInfo(arrayIndexerExpr.Indices[i]).ExpressionType;
2829                 object argValue = RuleExpressionWalker.Evaluate(execution, arrayIndexerExpr.Indices[i]).Value;
2830                 indexArgs[i] = (long)Executor.AdjustType(argType, argValue, typeof(long));
2831             }
2832 
2833             RuleArrayElementResult result = new RuleArrayElementResult((Array)target, indexArgs);
2834             return result;
2835         }
2836 
Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)2837         internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
2838         {
2839             string message;
2840 
2841             CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
2842 
2843             CodeExpression targetObject = arrayIndexerExpr.TargetObject;
2844             if (targetObject == null)
2845             {
2846                 message = string.Format(CultureInfo.CurrentCulture, Messages.NullIndexerTarget);
2847                 RuleEvaluationException exception = new RuleEvaluationException(message);
2848                 exception.Data[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
2849                 throw exception;
2850             }
2851 
2852             if (arrayIndexerExpr.Indices == null || arrayIndexerExpr.Indices.Count == 0)
2853             {
2854                 message = string.Format(CultureInfo.CurrentCulture, Messages.MissingIndexExpressions);
2855                 RuleEvaluationException exception = new RuleEvaluationException(message);
2856                 exception.Data[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
2857                 throw exception;
2858             }
2859 
2860             RuleExpressionWalker.Decompile(stringBuilder, targetObject, arrayIndexerExpr);
2861             stringBuilder.Append('[');
2862             RuleExpressionWalker.Decompile(stringBuilder, arrayIndexerExpr.Indices[0], null);
2863             for (int i = 1; i < arrayIndexerExpr.Indices.Count; ++i)
2864             {
2865                 stringBuilder.Append(", ");
2866                 RuleExpressionWalker.Decompile(stringBuilder, arrayIndexerExpr.Indices[i], null);
2867             }
2868             stringBuilder.Append(']');
2869         }
2870 
Clone(CodeExpression expression)2871         internal override CodeExpression Clone(CodeExpression expression)
2872         {
2873             CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
2874 
2875             CodeExpression targetObject = RuleExpressionWalker.Clone(arrayIndexerExpr.TargetObject);
2876 
2877             CodeExpression[] indices = new CodeExpression[arrayIndexerExpr.Indices.Count];
2878             for (int i = 0; i < indices.Length; ++i)
2879                 indices[i] = RuleExpressionWalker.Clone(arrayIndexerExpr.Indices[i]);
2880 
2881             CodeArrayIndexerExpression newIndexer = new CodeArrayIndexerExpression(targetObject, indices);
2882             return newIndexer;
2883         }
2884 
Match(CodeExpression expression, CodeExpression comperand)2885         internal override bool Match(CodeExpression expression, CodeExpression comperand)
2886         {
2887             CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
2888 
2889             CodeArrayIndexerExpression indexerComperand = (CodeArrayIndexerExpression)comperand;
2890             if (!RuleExpressionWalker.Match(arrayIndexerExpr.TargetObject, indexerComperand.TargetObject))
2891                 return false;
2892 
2893             if (arrayIndexerExpr.Indices.Count != indexerComperand.Indices.Count)
2894                 return false;
2895 
2896             for (int i = 0; i < arrayIndexerExpr.Indices.Count; ++i)
2897             {
2898                 if (!RuleExpressionWalker.Match(arrayIndexerExpr.Indices[i], indexerComperand.Indices[i]))
2899                     return false;
2900             }
2901 
2902             return true;
2903         }
2904     }
2905 
2906     #endregion
2907 
2908     #region Object Create expression
2909     internal class ObjectCreateExpression : RuleExpressionInternal
2910     {
Validate(CodeExpression expression, RuleValidation validation, bool isWritten)2911         internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
2912         {
2913             string message;
2914             ValidationError error;
2915 
2916             CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
2917 
2918             if (isWritten)
2919             {
2920                 message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeObjectCreateExpression).ToString());
2921                 error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
2922                 error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
2923                 validation.Errors.Add(error);
2924                 return null;
2925             }
2926 
2927             if (createExpression.CreateType == null)
2928             {
2929                 error = new ValidationError(Messages.NullTypeType, ErrorNumbers.Error_ParameterNotSet);
2930                 error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
2931                 validation.Errors.Add(error);
2932                 return null;
2933             }
2934 
2935             Type resultType = validation.ResolveType(createExpression.CreateType);
2936             if (resultType == null)
2937                 return null;
2938 
2939             // look up parameters
2940             List<CodeExpression> parameters = new List<CodeExpression>();
2941             try
2942             {
2943                 // Early exit from this if a cycle is detected.
2944                 if (!validation.PushParentExpression(createExpression))
2945                     return null;
2946 
2947                 bool hasInvalidArgument = false;
2948                 for (int i = 0; i < createExpression.Parameters.Count; ++i)
2949                 {
2950                     CodeExpression parameter = createExpression.Parameters[i];
2951                     if (parameter == null)
2952                     {
2953                         message = string.Format(CultureInfo.CurrentCulture, Messages.NullConstructorParameter, i.ToString(CultureInfo.CurrentCulture), RuleDecompiler.DecompileType(resultType));
2954                         error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
2955                         error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
2956                         validation.Errors.Add(error);
2957                         hasInvalidArgument = true;
2958                     }
2959                     else
2960                     {
2961                         RuleExpressionInfo parameterInfo = RuleExpressionWalker.Validate(validation, parameter, false);
2962                         if (parameterInfo == null)
2963                             hasInvalidArgument = true;
2964                         parameters.Add(parameter);
2965                     }
2966                 }
2967                 // quit if parameters not valid
2968                 if (hasInvalidArgument)
2969                     return null;
2970             }
2971             finally
2972             {
2973                 validation.PopParentExpression();
2974             }
2975 
2976             // see if we can find the matching constructor
2977             BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;
2978             if (validation.AllowInternalMembers(resultType))
2979                 bindingFlags |= BindingFlags.NonPublic;
2980 
2981             // creating a value-type object with no parameters can always be done
2982             if ((resultType.IsValueType) && (parameters.Count == 0))
2983                 return new RuleExpressionInfo(resultType);
2984 
2985             // error if type is an abstract type
2986             if (resultType.IsAbstract)
2987             {
2988                 message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownConstructor, RuleDecompiler.DecompileType(resultType));
2989                 error = new ValidationError(message, ErrorNumbers.Error_MethodNotExists);
2990                 error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
2991                 validation.Errors.Add(error);
2992                 return null;
2993             }
2994 
2995             RuleConstructorExpressionInfo constructorInvokeInfo = validation.ResolveConstructor(resultType, bindingFlags, parameters, out error);
2996             if (constructorInvokeInfo == null)
2997             {
2998                 message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownConstructor, RuleDecompiler.DecompileType(resultType));
2999                 error = new ValidationError(message, ErrorNumbers.Error_MethodNotExists);
3000                 error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
3001                 validation.Errors.Add(error);
3002                 return null;
3003             }
3004 
3005             return constructorInvokeInfo;
3006         }
3007 
AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)3008         internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
3009         {
3010             CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
3011 
3012             // check each parameter
3013             foreach (CodeExpression p in createExpression.Parameters)
3014             {
3015                 RuleExpressionWalker.AnalyzeUsage(analysis, p, true, false, null);
3016             }
3017         }
3018 
Evaluate(CodeExpression expression, RuleExecution execution)3019         internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
3020         {
3021             CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
3022 
3023             if (createExpression.CreateType == null)
3024             {
3025                 RuleEvaluationException exception = new RuleEvaluationException(Messages.NullTypeType);
3026                 exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
3027                 throw exception;
3028             }
3029 
3030             RuleExpressionInfo expressionInfo = execution.Validation.ExpressionInfo(createExpression);
3031             if (expressionInfo == null)  // Oops, someone forgot to validate.
3032             {
3033                 InvalidOperationException exception = new InvalidOperationException(Messages.ExpressionNotValidated);
3034                 exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
3035                 throw exception;
3036             }
3037 
3038             RuleConstructorExpressionInfo createExpressionInfo = expressionInfo as RuleConstructorExpressionInfo;
3039             if (createExpressionInfo == null)
3040             {
3041                 // it's just a regular RuleExpressionInfo, which means this is a value-type with no parameters
3042                 return new RuleLiteralResult(Activator.CreateInstance(expressionInfo.ExpressionType));
3043             }
3044 
3045             ConstructorInfo constructor = createExpressionInfo.ConstructorInfo;
3046             object[] arguments = null;
3047             RuleExpressionResult[] outArgumentResults = null;
3048 
3049             if (createExpression.Parameters != null && createExpression.Parameters.Count > 0)
3050             {
3051                 int actualArgCount = createExpression.Parameters.Count;
3052                 ParameterInfo[] parmInfos = constructor.GetParameters();
3053 
3054                 arguments = new object[parmInfos.Length];
3055 
3056                 int numFixedParameters = parmInfos.Length;
3057                 if (createExpressionInfo.NeedsParamsExpansion)
3058                     numFixedParameters -= 1;
3059 
3060                 int i;
3061 
3062                 // Evaluate the fixed portion of the parameter list.
3063                 for (i = 0; i < numFixedParameters; ++i)
3064                 {
3065                     Type argType = execution.Validation.ExpressionInfo(createExpression.Parameters[i]).ExpressionType;
3066                     RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, createExpression.Parameters[i]);
3067 
3068                     // Special procesing of direction expressions to keep track of out arguments (& ref).
3069                     CodeDirectionExpression direction = createExpression.Parameters[i] as CodeDirectionExpression;
3070                     if (direction != null && (direction.Direction == FieldDirection.Ref || direction.Direction == FieldDirection.Out))
3071                     {
3072                         // lazy creation of fieldsToSet
3073                         if (outArgumentResults == null)
3074                             outArgumentResults = new RuleExpressionResult[actualArgCount];
3075                         // keep track of this out expression so we can set it later
3076                         outArgumentResults[i] = argResult;
3077                     }
3078 
3079                     arguments[i] = Executor.AdjustType(argType, argResult.Value, parmInfos[i].ParameterType);
3080                 }
3081 
3082                 if (numFixedParameters < actualArgCount)
3083                 {
3084                     // This target method had a params array, and we are calling it with an
3085                     // expanded parameter list.  E.g.,
3086                     //      void foo(int x, params string[] y)
3087                     // with the invocation:
3088                     //      foo(5, "crud", "kreeble", "glorp")
3089                     // We need to translate this to:
3090                     //      foo(5, new string[] { "crud", "kreeble", "glorp" })
3091 
3092                     ParameterInfo lastParamInfo = parmInfos[numFixedParameters];
3093 
3094                     Type arrayType = lastParamInfo.ParameterType;
3095                     System.Diagnostics.Debug.Assert(arrayType.IsArray);
3096                     Type elementType = arrayType.GetElementType();
3097 
3098                     Array paramsArray = Array.CreateInstance(elementType, actualArgCount - i);
3099                     for (; i < actualArgCount; ++i)
3100                     {
3101                         Type argType = execution.Validation.ExpressionInfo(createExpression.Parameters[i]).ExpressionType;
3102                         RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, createExpression.Parameters[i]);
3103                         paramsArray.SetValue(Executor.AdjustType(argType, argResult.Value, elementType), i - numFixedParameters);
3104                     }
3105 
3106                     arguments[numFixedParameters] = paramsArray;
3107                 }
3108             }
3109 
3110             object result;
3111             try
3112             {
3113                 result = constructor.Invoke(arguments);
3114             }
3115             catch (TargetInvocationException e)
3116             {
3117                 // if there is no inner exception, leave it untouched
3118                 if (e.InnerException == null)
3119                     throw;
3120                 string message = string.Format(CultureInfo.CurrentCulture,
3121                     Messages.Error_ConstructorInvoke,
3122                     RuleDecompiler.DecompileType(createExpressionInfo.ExpressionType),
3123                     e.InnerException.Message);
3124                 throw new TargetInvocationException(message, e.InnerException);
3125             }
3126 
3127             // any out/ref parameters that need to be assigned?
3128             if (outArgumentResults != null)
3129             {
3130                 for (int i = 0; i < createExpression.Parameters.Count; ++i)
3131                 {
3132                     if (outArgumentResults[i] != null)
3133                         outArgumentResults[i].Value = arguments[i];
3134                 }
3135             }
3136             return new RuleLiteralResult(result);
3137         }
3138 
Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)3139         internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
3140         {
3141             CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
3142 
3143             bool mustParenthesize = RuleDecompiler.MustParenthesize(createExpression, parentExpression);
3144             if (mustParenthesize)
3145                 stringBuilder.Append("(");
3146 
3147             stringBuilder.Append("new ");
3148             RuleDecompiler.DecompileType(stringBuilder, createExpression.CreateType);
3149 
3150             // Decompile the arguments
3151             stringBuilder.Append('(');
3152             for (int i = 0; i < createExpression.Parameters.Count; ++i)
3153             {
3154                 CodeExpression paramExpr = createExpression.Parameters[i];
3155                 if (paramExpr == null)
3156                 {
3157                     string message = string.Format(CultureInfo.CurrentCulture, Messages.NullConstructorTypeParameter, i.ToString(CultureInfo.CurrentCulture), createExpression.CreateType);
3158                     RuleEvaluationException exception = new RuleEvaluationException(message);
3159                     exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
3160                     throw exception;
3161                 }
3162 
3163                 if (i > 0)
3164                     stringBuilder.Append(", ");
3165 
3166                 RuleExpressionWalker.Decompile(stringBuilder, paramExpr, null);
3167             }
3168             stringBuilder.Append(')');
3169 
3170             if (mustParenthesize)
3171                 stringBuilder.Append(")");
3172         }
3173 
Clone(CodeExpression expression)3174         internal override CodeExpression Clone(CodeExpression expression)
3175         {
3176             CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
3177 
3178             CodeObjectCreateExpression newCreate = new CodeObjectCreateExpression();
3179             newCreate.CreateType = TypeReferenceExpression.CloneType(createExpression.CreateType);
3180             foreach (CodeExpression p in createExpression.Parameters)
3181             {
3182                 newCreate.Parameters.Add(RuleExpressionWalker.Clone(p));
3183             }
3184             return newCreate;
3185         }
3186 
Match(CodeExpression expression, CodeExpression comperand)3187         internal override bool Match(CodeExpression expression, CodeExpression comperand)
3188         {
3189             CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
3190 
3191             CodeObjectCreateExpression createComperand = comperand as CodeObjectCreateExpression;
3192             if (createComperand == null)
3193                 return false;
3194             // check types
3195             if (!TypeReferenceExpression.MatchType(createExpression.CreateType, createComperand.CreateType))
3196                 return false;
3197             // check parameters
3198             if (createExpression.Parameters.Count != createComperand.Parameters.Count)
3199                 return false;
3200             for (int i = 0; i < createExpression.Parameters.Count; ++i)
3201                 if (!RuleExpressionWalker.Match(createExpression.Parameters[i], createComperand.Parameters[i]))
3202                     return false;
3203             return true;
3204         }
3205     }
3206     #endregion
3207 
3208     #region Array Create expression
3209     internal class ArrayCreateExpression : RuleExpressionInternal
3210     {
3211         [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
Validate(CodeExpression expression, RuleValidation validation, bool isWritten)3212         internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
3213         {
3214             string message;
3215 
3216             CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
3217 
3218             if (isWritten)
3219             {
3220                 message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeObjectCreateExpression).ToString());
3221                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
3222                 error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
3223                 validation.Errors.Add(error);
3224                 return null;
3225             }
3226 
3227             if (createExpression.CreateType == null)
3228             {
3229                 ValidationError error = new ValidationError(Messages.NullTypeType, ErrorNumbers.Error_ParameterNotSet);
3230                 error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
3231                 validation.Errors.Add(error);
3232                 return null;
3233             }
3234 
3235             Type resultType = validation.ResolveType(createExpression.CreateType);
3236             if (resultType == null)
3237                 return null;
3238 
3239             // size CodeDom has limited support for arrays (only 1 dimensional, not more)
3240             // we limit CodeArrayCreateExpression to a single dimension
3241             // (i.e. CodeArrayCreateExpression cannot define int[5,3] or int[5][3],
3242             // but it is possible to define int[][3] and then use initializers to
3243             // fill it in. But we only support int[] or int[3].)
3244             if (resultType.IsArray)
3245             {
3246                 message = string.Format(CultureInfo.CurrentCulture, Messages.ArrayTypeInvalid, resultType.Name);
3247                 ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
3248                 error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
3249                 validation.Errors.Add(error);
3250                 return null;
3251             }
3252 
3253             try
3254             {
3255                 // Early exit from this if a cycle is detected.
3256                 if (!validation.PushParentExpression(createExpression))
3257                     return null;
3258 
3259                 if (createExpression.Size < 0)
3260                 {
3261                     ValidationError error = new ValidationError(Messages.ArraySizeInvalid, ErrorNumbers.Error_ParameterNotSet);
3262                     error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
3263                     validation.Errors.Add(error);
3264                     return null;
3265                 }
3266 
3267                 // look up size (if specified)
3268                 if (createExpression.SizeExpression != null)
3269                 {
3270                     RuleExpressionInfo sizeInfo = RuleExpressionWalker.Validate(validation, createExpression.SizeExpression, false);
3271                     if (sizeInfo == null)
3272                         return null;
3273                     if ((sizeInfo.ExpressionType != typeof(int))
3274                         && (sizeInfo.ExpressionType != typeof(uint))
3275                         && (sizeInfo.ExpressionType != typeof(long))
3276                         && (sizeInfo.ExpressionType != typeof(ulong)))
3277                     {
3278                         message = string.Format(CultureInfo.CurrentCulture, Messages.ArraySizeTypeInvalid, sizeInfo.ExpressionType.Name);
3279                         ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
3280                         error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
3281                         validation.Errors.Add(error);
3282                         return null;
3283                     }
3284                 }
3285                 bool parameterInvalid = false;
3286                 for (int i = 0; i < createExpression.Initializers.Count; ++i)
3287                 {
3288                     CodeExpression init = createExpression.Initializers[i];
3289                     if (init == null)
3290                     {
3291                         message = string.Format(CultureInfo.CurrentCulture, Messages.MissingInitializer, resultType.Name);
3292                         ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
3293                         error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
3294                         validation.Errors.Add(error);
3295                         return null;
3296                     }
3297                     RuleExpressionInfo parameterInfo = RuleExpressionWalker.Validate(validation, init, false);
3298                     if (parameterInfo == null)
3299                     {
3300                         parameterInvalid = true;
3301                     }
3302                     else
3303                     {
3304                         // can we convert the result type to the array type?
3305                         ValidationError error;
3306                         if (!RuleValidation.StandardImplicitConversion(parameterInfo.ExpressionType, resultType, init, out error))
3307                         {
3308                             // types must match
3309                             if (error != null)
3310                             {
3311                                 // we got an error from the conversion, so give it back as well as a new error
3312                                 error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
3313                                 validation.Errors.Add(error);
3314                             }
3315                             message = string.Format(CultureInfo.CurrentCulture, Messages.InitializerMismatch, i, resultType.Name);
3316                             error = new ValidationError(message, ErrorNumbers.Error_OperandTypesIncompatible);
3317                             error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
3318                             validation.Errors.Add(error);
3319                             return null;
3320                         }
3321                     }
3322                 }
3323                 // if any errors get out
3324                 if (parameterInvalid)
3325                     return null;
3326 
3327                 // now it gets tricky. CodeArrayCreateExpression constructors allow:
3328                 //     1) size as int
3329                 //     2) size as CodeExpression
3330                 //     3) initializers as params array
3331                 // However, we allow a size and initializers, so try to verify size >= #initializers
3332                 // size can be an int, uint, long, or ulong
3333                 double size = -1;
3334                 if (createExpression.SizeExpression != null)
3335                 {
3336                     CodePrimitiveExpression prim = createExpression.SizeExpression as CodePrimitiveExpression;
3337                     if ((prim != null) && (prim.Value != null))
3338                         size = (double)Executor.AdjustType(prim.Value.GetType(), prim.Value, typeof(double));
3339                     if (createExpression.Size > 0)
3340                     {
3341                         // both size and SizeExpression specified, complain
3342                         ValidationError error = new ValidationError(Messages.ArraySizeBoth, ErrorNumbers.Error_ParameterNotSet);
3343                         error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
3344                         validation.Errors.Add(error);
3345                         return null;
3346                     }
3347                 }
3348                 else if (createExpression.Size > 0)
3349                     size = createExpression.Size;
3350 
3351                 if ((size >= 0) && (createExpression.Initializers.Count > size))
3352                 {
3353                     message = string.Format(CultureInfo.CurrentCulture, Messages.InitializerCountMismatch, createExpression.Initializers.Count, size);
3354                     ValidationError error = new ValidationError(message, ErrorNumbers.Error_OperandTypesIncompatible);
3355                     error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
3356                     validation.Errors.Add(error);
3357                     return null;
3358                 }
3359             }
3360             finally
3361             {
3362                 validation.PopParentExpression();
3363             }
3364             return new RuleExpressionInfo(resultType.MakeArrayType());
3365         }
3366 
AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)3367         internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
3368         {
3369             CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
3370 
3371             if (createExpression.SizeExpression != null)
3372                 RuleExpressionWalker.AnalyzeUsage(analysis, createExpression.SizeExpression, true, false, null);
3373             foreach (CodeExpression p in createExpression.Initializers)
3374                 RuleExpressionWalker.AnalyzeUsage(analysis, p, true, false, null);
3375         }
3376 
Evaluate(CodeExpression expression, RuleExecution execution)3377         internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
3378         {
3379             CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
3380 
3381             if (createExpression.CreateType == null)
3382             {
3383                 RuleEvaluationException exception = new RuleEvaluationException(Messages.NullTypeType);
3384                 exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
3385                 throw exception;
3386             }
3387 
3388             RuleExpressionInfo createExpressionInfo = execution.Validation.ExpressionInfo(createExpression);
3389             if (createExpression == null)  // Oops, someone forgot to validate.
3390             {
3391                 InvalidOperationException exception = new InvalidOperationException(Messages.ExpressionNotValidated);
3392                 exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
3393                 throw exception;
3394             }
3395 
3396             // type should be an array type already
3397             Type type = createExpressionInfo.ExpressionType;
3398             Type elementType = type.GetElementType();
3399 
3400             // assume this has been validated, so only 1 size specified
3401             int size = 0;
3402             if (createExpression.SizeExpression != null)
3403             {
3404                 Type sizeType = execution.Validation.ExpressionInfo(createExpression.SizeExpression).ExpressionType;
3405                 RuleExpressionResult sizeResult = RuleExpressionWalker.Evaluate(execution, createExpression.SizeExpression);
3406                 if (sizeType == typeof(int))
3407                     size = (int)sizeResult.Value;
3408                 else if (sizeType == typeof(long))
3409                     size = (int)((long)sizeResult.Value);
3410                 else if (sizeType == typeof(uint))
3411                     size = (int)((uint)sizeResult.Value);
3412                 else if (sizeType == typeof(ulong))
3413                     size = (int)((ulong)sizeResult.Value);
3414             }
3415             else if (createExpression.Size != 0)
3416                 size = createExpression.Size;
3417             else
3418                 size = createExpression.Initializers.Count;
3419 
3420             Array result = Array.CreateInstance(elementType, size);
3421             if (createExpression.Initializers != null)
3422                 for (int i = 0; i < createExpression.Initializers.Count; ++i)
3423                 {
3424                     CodeExpression initializer = createExpression.Initializers[i];
3425                     Type initializerType = execution.Validation.ExpressionInfo(initializer).ExpressionType;
3426                     RuleExpressionResult initializerResult = RuleExpressionWalker.Evaluate(execution, initializer);
3427                     result.SetValue(Executor.AdjustType(initializerType, initializerResult.Value, elementType), i);
3428                 }
3429             return new RuleLiteralResult(result);
3430         }
3431 
Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)3432         internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
3433         {
3434             CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
3435 
3436             bool mustParenthesize = RuleDecompiler.MustParenthesize(createExpression, parentExpression);
3437             if (mustParenthesize)
3438                 stringBuilder.Append("(");
3439 
3440             stringBuilder.Append("new ");
3441             RuleDecompiler.DecompileType(stringBuilder, createExpression.CreateType);
3442             stringBuilder.Append('[');
3443             if (createExpression.SizeExpression != null)
3444                 RuleExpressionWalker.Decompile(stringBuilder, createExpression.SizeExpression, null);
3445             else if ((createExpression.Size != 0) || (createExpression.Initializers.Count == 0))
3446                 stringBuilder.Append(createExpression.Size);
3447             stringBuilder.Append(']');
3448             if (createExpression.Initializers.Count > 0)
3449             {
3450                 stringBuilder.Append(" { ");
3451                 for (int i = 0; i < createExpression.Initializers.Count; ++i)
3452                 {
3453                     CodeExpression paramExpr = createExpression.Initializers[i];
3454                     if (paramExpr == null)
3455                     {
3456                         string message = string.Format(CultureInfo.CurrentCulture, Messages.NullConstructorTypeParameter, i.ToString(CultureInfo.CurrentCulture), createExpression.CreateType);
3457                         RuleEvaluationException exception = new RuleEvaluationException(message);
3458                         exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
3459                         throw exception;
3460                     }
3461 
3462                     if (i > 0)
3463                         stringBuilder.Append(", ");
3464 
3465                     RuleExpressionWalker.Decompile(stringBuilder, paramExpr, null);
3466                 }
3467                 stringBuilder.Append('}');
3468             }
3469 
3470             if (mustParenthesize)
3471                 stringBuilder.Append(")");
3472         }
3473 
Clone(CodeExpression expression)3474         internal override CodeExpression Clone(CodeExpression expression)
3475         {
3476             CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
3477 
3478             CodeArrayCreateExpression newCreate = new CodeArrayCreateExpression();
3479             newCreate.CreateType = TypeReferenceExpression.CloneType(createExpression.CreateType);
3480             newCreate.Size = createExpression.Size;
3481             if (createExpression.SizeExpression != null)
3482                 newCreate.SizeExpression = RuleExpressionWalker.Clone(createExpression.SizeExpression);
3483             foreach (CodeExpression p in createExpression.Initializers)
3484                 newCreate.Initializers.Add(RuleExpressionWalker.Clone(p));
3485             return newCreate;
3486         }
3487 
Match(CodeExpression expression, CodeExpression comperand)3488         internal override bool Match(CodeExpression expression, CodeExpression comperand)
3489         {
3490             CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
3491 
3492             CodeArrayCreateExpression createComperand = comperand as CodeArrayCreateExpression;
3493             if ((createComperand == null)
3494                 || (createExpression.Size != createComperand.Size)
3495                 || (!TypeReferenceExpression.MatchType(createExpression.CreateType, createComperand.CreateType)))
3496                 return false;
3497             // check SizeExpression, if it exists
3498             if (createExpression.SizeExpression != null)
3499             {
3500                 if (createComperand.SizeExpression == null)
3501                     return false;
3502                 if (!RuleExpressionWalker.Match(createExpression.SizeExpression, createComperand.SizeExpression))
3503                     return false;
3504             }
3505             else
3506             {
3507                 if (createComperand.SizeExpression != null)
3508                     return false;
3509             }
3510             // check initializers
3511             if (createExpression.Initializers.Count != createComperand.Initializers.Count)
3512                 return false;
3513             for (int i = 0; i < createExpression.Initializers.Count; ++i)
3514                 if (!RuleExpressionWalker.Match(createExpression.Initializers[i], createComperand.Initializers[i]))
3515                     return false;
3516             return true;
3517         }
3518     }
3519     #endregion
3520 }
3521