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