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.Configuration; 9 using System.Diagnostics; 10 using System.Diagnostics.CodeAnalysis; 11 using System.Globalization; 12 using System.Reflection; 13 using System.Text; 14 using System.Workflow.ComponentModel; 15 using System.Workflow.ComponentModel.Compiler; 16 using System.Workflow.Activities.Common; 17 18 namespace System.Workflow.Activities.Rules 19 { 20 #region ExpressionInfo 21 22 // Public base class (which just holds the Type of the expression). 23 public class RuleExpressionInfo 24 { 25 private Type expressionType; 26 RuleExpressionInfo(Type expressionType)27 public RuleExpressionInfo(Type expressionType) 28 { 29 this.expressionType = expressionType; 30 } 31 32 public Type ExpressionType 33 { 34 get { return expressionType; } 35 } 36 } 37 38 // Internal derivation for CodeMethodInvokeExpression 39 internal class RuleMethodInvokeExpressionInfo : RuleExpressionInfo 40 { 41 private MethodInfo methodInfo; 42 private bool needsParamsExpansion; 43 RuleMethodInvokeExpressionInfo(MethodInfo mi, bool needsParamsExpansion)44 internal RuleMethodInvokeExpressionInfo(MethodInfo mi, bool needsParamsExpansion) 45 : base(mi.ReturnType) 46 { 47 this.methodInfo = mi; 48 this.needsParamsExpansion = needsParamsExpansion; 49 } 50 51 internal MethodInfo MethodInfo 52 { 53 get { return methodInfo; } 54 } 55 56 internal bool NeedsParamsExpansion 57 { 58 get { return needsParamsExpansion; } 59 } 60 } 61 62 // Internal derivation for CodeBinaryExpression 63 internal class RuleBinaryExpressionInfo : RuleExpressionInfo 64 { 65 private Type leftType; 66 private Type rightType; 67 private MethodInfo methodInfo; 68 69 // no overridden method needed RuleBinaryExpressionInfo(Type lhsType, Type rhsType, Type resultType)70 internal RuleBinaryExpressionInfo(Type lhsType, Type rhsType, Type resultType) 71 : base(resultType) 72 { 73 this.leftType = lhsType; 74 this.rightType = rhsType; 75 } 76 77 // overridden method found RuleBinaryExpressionInfo(Type lhsType, Type rhsType, MethodInfo mi)78 internal RuleBinaryExpressionInfo(Type lhsType, Type rhsType, MethodInfo mi) 79 : base(mi.ReturnType) 80 { 81 this.leftType = lhsType; 82 this.rightType = rhsType; 83 this.methodInfo = mi; 84 } 85 86 internal Type LeftType 87 { 88 get { return leftType; } 89 } 90 91 internal Type RightType 92 { 93 get { return rightType; } 94 } 95 96 internal MethodInfo MethodInfo 97 { 98 get { return methodInfo; } 99 } 100 } 101 102 // Internal derivation for CodeFieldReferenceExpression 103 internal class RuleFieldExpressionInfo : RuleExpressionInfo 104 { 105 private FieldInfo fieldInfo; 106 RuleFieldExpressionInfo(FieldInfo fi)107 internal RuleFieldExpressionInfo(FieldInfo fi) 108 : base(fi.FieldType) 109 { 110 fieldInfo = fi; 111 } 112 113 internal FieldInfo FieldInfo 114 { 115 get { return fieldInfo; } 116 } 117 } 118 119 // Internal derivation for CodePropertyReferenceExpression 120 internal class RulePropertyExpressionInfo : RuleExpressionInfo 121 { 122 private PropertyInfo propertyInfo; 123 private bool needsParamsExpansion; 124 125 // Note that the type pi.PropertyType may differ from the "exprType" argument if this 126 // property is a Bind. RulePropertyExpressionInfo(PropertyInfo pi, Type exprType, bool needsParamsExpansion)127 internal RulePropertyExpressionInfo(PropertyInfo pi, Type exprType, bool needsParamsExpansion) 128 : base(exprType) 129 { 130 this.propertyInfo = pi; 131 this.needsParamsExpansion = needsParamsExpansion; 132 } 133 134 internal PropertyInfo PropertyInfo 135 { 136 get { return propertyInfo; } 137 } 138 139 internal bool NeedsParamsExpansion 140 { 141 get { return needsParamsExpansion; } 142 } 143 } 144 145 // Internal derivation for CodeMethodInvokeExpression 146 internal class RuleConstructorExpressionInfo : RuleExpressionInfo 147 { 148 private ConstructorInfo constructorInfo; 149 private bool needsParamsExpansion; 150 RuleConstructorExpressionInfo(ConstructorInfo ci, bool needsParamsExpansion)151 internal RuleConstructorExpressionInfo(ConstructorInfo ci, bool needsParamsExpansion) 152 : base(ci.DeclaringType) 153 { 154 this.constructorInfo = ci; 155 this.needsParamsExpansion = needsParamsExpansion; 156 } 157 158 internal ConstructorInfo ConstructorInfo 159 { 160 get { return constructorInfo; } 161 } 162 163 internal bool NeedsParamsExpansion 164 { 165 get { return needsParamsExpansion; } 166 } 167 } 168 169 internal class ExtensionMethodInfo : MethodInfo 170 { 171 MethodInfo actualMethod; 172 int actualParameterLength; 173 ParameterInfo[] expectedParameters; 174 Type assumedDeclaringType; 175 bool hasOutOrRefParameters = false; 176 ExtensionMethodInfo(MethodInfo method, ParameterInfo[] actualParameters)177 public ExtensionMethodInfo(MethodInfo method, ParameterInfo[] actualParameters) 178 : base() 179 { 180 Debug.Assert(method.IsStatic, "Expected static method as an extension method"); 181 182 actualMethod = method; 183 // modify parameters 184 actualParameterLength = actualParameters.Length; 185 if (actualParameterLength < 2) 186 expectedParameters = new ParameterInfo[0]; 187 else 188 { 189 expectedParameters = new ParameterInfo[actualParameterLength - 1]; 190 Array.Copy(actualParameters, 1, expectedParameters, 0, actualParameterLength - 1); 191 foreach (ParameterInfo pi in expectedParameters) 192 { 193 if (pi.ParameterType.IsByRef) 194 hasOutOrRefParameters = true; 195 } 196 } 197 // get the type we pretend this method is on (which happens to be the first actual parameter) 198 assumedDeclaringType = actualParameters[0].ParameterType; 199 } 200 GetBaseDefinition()201 public override MethodInfo GetBaseDefinition() 202 { 203 return actualMethod.GetBaseDefinition(); 204 } 205 206 public override ICustomAttributeProvider ReturnTypeCustomAttributes 207 { 208 get { return actualMethod.ReturnTypeCustomAttributes; } 209 } 210 211 public override MethodAttributes Attributes 212 { 213 get { return actualMethod.Attributes & ~MethodAttributes.Static; } 214 } 215 GetMethodImplementationFlags()216 public override MethodImplAttributes GetMethodImplementationFlags() 217 { 218 return actualMethod.GetMethodImplementationFlags(); 219 } 220 GetParameters()221 public override ParameterInfo[] GetParameters() 222 { 223 return expectedParameters; 224 } 225 Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)226 public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) 227 { 228 object[] actualParameters = new object[actualParameterLength]; 229 if (actualParameterLength > 1) 230 Array.Copy(parameters, 0, actualParameters, 1, actualParameterLength - 1); 231 if (obj == null) 232 actualParameters[0] = null; 233 else 234 actualParameters[0] = Executor.AdjustType(obj.GetType(), obj, assumedDeclaringType); 235 object result = actualMethod.Invoke(null, invokeAttr, binder, actualParameters, culture); 236 // may be out/ref parameters, so copy back the results 237 if (hasOutOrRefParameters) 238 Array.Copy(actualParameters, 1, parameters, 0, actualParameterLength - 1); 239 return result; 240 } 241 242 public override RuntimeMethodHandle MethodHandle 243 { 244 get { return actualMethod.MethodHandle; } 245 } 246 247 public override Type DeclaringType 248 { 249 get { return actualMethod.DeclaringType; } 250 } 251 252 public Type AssumedDeclaringType 253 { 254 get { return assumedDeclaringType; } 255 } 256 GetCustomAttributes(Type attributeType, bool inherit)257 public override object[] GetCustomAttributes(Type attributeType, bool inherit) 258 { 259 return actualMethod.GetCustomAttributes(attributeType, inherit); 260 } 261 GetCustomAttributes(bool inherit)262 public override object[] GetCustomAttributes(bool inherit) 263 { 264 return actualMethod.GetCustomAttributes(inherit); 265 } 266 IsDefined(Type attributeType, bool inherit)267 public override bool IsDefined(Type attributeType, bool inherit) 268 { 269 return actualMethod.IsDefined(attributeType, inherit); 270 } 271 272 public override string Name 273 { 274 get { return actualMethod.Name; } 275 } 276 277 public override Type ReflectedType 278 { 279 get { return actualMethod.ReflectedType; } 280 } 281 282 public override Type ReturnType 283 { 284 get { return actualMethod.ReturnType; } 285 } 286 } 287 288 internal class SimpleParameterInfo : ParameterInfo 289 { 290 // only thing we look at is ParameterType, so no need to override anything else 291 Type parameterType; 292 SimpleParameterInfo(ParameterInfo parameter)293 public SimpleParameterInfo(ParameterInfo parameter) 294 : base() 295 { 296 parameterType = typeof(Nullable<>).MakeGenericType(parameter.ParameterType); 297 } 298 SimpleParameterInfo(Type parameter)299 public SimpleParameterInfo(Type parameter) 300 : base() 301 { 302 parameterType = parameter; 303 } 304 305 public override Type ParameterType 306 { 307 get 308 { 309 return parameterType; 310 } 311 } 312 } 313 314 internal abstract class BaseMethodInfo : MethodInfo 315 { 316 protected MethodInfo actualMethod; 317 protected ParameterInfo[] expectedParameters; 318 protected Type resultType; 319 BaseMethodInfo(MethodInfo method)320 public BaseMethodInfo(MethodInfo method) 321 : base() 322 { 323 Debug.Assert(method.IsStatic, "Expected static method as an lifted method"); 324 actualMethod = method; 325 resultType = method.ReturnType; 326 expectedParameters = method.GetParameters(); 327 } 328 GetBaseDefinition()329 public override MethodInfo GetBaseDefinition() 330 { 331 return actualMethod.GetBaseDefinition(); 332 } 333 334 public override ICustomAttributeProvider ReturnTypeCustomAttributes 335 { 336 get { return actualMethod.ReturnTypeCustomAttributes; } 337 } 338 339 public override MethodAttributes Attributes 340 { 341 get { return actualMethod.Attributes & ~MethodAttributes.Static; } 342 } 343 GetMethodImplementationFlags()344 public override MethodImplAttributes GetMethodImplementationFlags() 345 { 346 return actualMethod.GetMethodImplementationFlags(); 347 } 348 GetParameters()349 public override ParameterInfo[] GetParameters() 350 { 351 return expectedParameters; 352 } 353 Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)354 public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) 355 { 356 throw new NotImplementedException(); 357 } 358 359 public override RuntimeMethodHandle MethodHandle 360 { 361 get { return actualMethod.MethodHandle; } 362 } 363 364 public override Type DeclaringType 365 { 366 get { return actualMethod.DeclaringType; } 367 } 368 GetCustomAttributes(Type attributeType, bool inherit)369 public override object[] GetCustomAttributes(Type attributeType, bool inherit) 370 { 371 return actualMethod.GetCustomAttributes(attributeType, inherit); 372 } 373 GetCustomAttributes(bool inherit)374 public override object[] GetCustomAttributes(bool inherit) 375 { 376 return actualMethod.GetCustomAttributes(inherit); 377 } 378 IsDefined(Type attributeType, bool inherit)379 public override bool IsDefined(Type attributeType, bool inherit) 380 { 381 return actualMethod.IsDefined(attributeType, inherit); 382 } 383 384 public override string Name 385 { 386 get { return actualMethod.Name; } 387 } 388 389 public override Type ReflectedType 390 { 391 get { return actualMethod.ReflectedType; } 392 } 393 394 public override Type ReturnType 395 { 396 get { return resultType; } 397 } 398 Equals(object obj)399 public override bool Equals(object obj) 400 { 401 BaseMethodInfo other = obj as BaseMethodInfo; 402 if ((other == null) 403 || (actualMethod != other.actualMethod) 404 || (resultType != other.resultType) 405 || (expectedParameters.Length != other.expectedParameters.Length)) 406 return false; 407 for (int i = 0; i < expectedParameters.Length; ++i) 408 if (expectedParameters[i].ParameterType != other.expectedParameters[i].ParameterType) 409 return false; 410 return true; 411 } 412 GetHashCode()413 public override int GetHashCode() 414 { 415 int result = actualMethod.GetHashCode() ^ resultType.GetHashCode(); 416 for (int i = 0; i < expectedParameters.Length; ++i) 417 result ^= expectedParameters[i].GetHashCode(); 418 return result; 419 } 420 } 421 422 internal class LiftedConversionMethodInfo : BaseMethodInfo 423 { LiftedConversionMethodInfo(MethodInfo method)424 public LiftedConversionMethodInfo(MethodInfo method) 425 : base(method) 426 { 427 Debug.Assert(expectedParameters.Length == 1, "not 1 parameters"); 428 429 // modify result 430 resultType = typeof(Nullable<>).MakeGenericType(method.ReturnType); 431 432 // modify parameter (exactly 1) 433 ParameterInfo[] actualParameters = method.GetParameters(); 434 expectedParameters = new ParameterInfo[1]; 435 expectedParameters[0] = new SimpleParameterInfo(actualParameters[0]); 436 } 437 Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)438 public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) 439 { 440 // null in, then result is null 441 if (parameters[0] == null) 442 return Activator.CreateInstance(resultType); 443 444 // invoke the conversion from S -> T 445 object result = actualMethod.Invoke(null, invokeAttr, binder, parameters, culture); 446 // return a T? 447 return Executor.AdjustType(actualMethod.ReturnType, result, resultType); 448 } 449 } 450 451 internal class LiftedArithmeticOperatorMethodInfo : BaseMethodInfo 452 { LiftedArithmeticOperatorMethodInfo(MethodInfo method)453 public LiftedArithmeticOperatorMethodInfo(MethodInfo method) 454 : base(method) 455 { 456 Debug.Assert(expectedParameters.Length == 2, "not 2 parameters"); 457 458 // modify parameters (exactly 2, both need to be lifted) 459 ParameterInfo[] actualParameters = method.GetParameters(); 460 expectedParameters = new ParameterInfo[2]; 461 expectedParameters[0] = new SimpleParameterInfo(actualParameters[0]); 462 expectedParameters[1] = new SimpleParameterInfo(actualParameters[1]); 463 464 // modify result 465 resultType = typeof(Nullable<>).MakeGenericType(method.ReturnType); 466 } 467 Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)468 public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) 469 { 470 // null in, then result is null 471 if (parameters[0] == null) 472 return null; 473 if (parameters[1] == null) 474 return null; 475 476 // apply the underlying operator 477 object result = actualMethod.Invoke(null, invokeAttr, binder, parameters, culture); 478 // return a T? 479 return Executor.AdjustType(actualMethod.ReturnType, result, resultType); 480 } 481 } 482 483 internal class LiftedEqualityOperatorMethodInfo : BaseMethodInfo 484 { LiftedEqualityOperatorMethodInfo(MethodInfo method)485 public LiftedEqualityOperatorMethodInfo(MethodInfo method) 486 : base(method) 487 { 488 Debug.Assert(method.ReturnType == typeof(bool), "not a bool result"); 489 Debug.Assert(expectedParameters.Length == 2, "not 2 parameters"); 490 491 // modify parameters (exactly 2, both need to be lifted) 492 ParameterInfo[] actualParameters = method.GetParameters(); 493 expectedParameters = new ParameterInfo[2]; 494 expectedParameters[0] = new SimpleParameterInfo(actualParameters[0]); 495 expectedParameters[1] = new SimpleParameterInfo(actualParameters[1]); 496 497 // set the result type 498 resultType = typeof(bool); 499 } 500 Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)501 public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) 502 { 503 // null == null is true, null == something else is false, else call method 504 if (parameters[0] == null) 505 return (parameters[1] == null); 506 else if (parameters[1] == null) 507 return false; 508 509 // invoke the actual comparison (parameters are unwrapped) 510 return actualMethod.Invoke(null, invokeAttr, binder, parameters, culture); 511 } 512 } 513 514 internal class LiftedRelationalOperatorMethodInfo : BaseMethodInfo 515 { LiftedRelationalOperatorMethodInfo(MethodInfo method)516 public LiftedRelationalOperatorMethodInfo(MethodInfo method) 517 : base(method) 518 { 519 Debug.Assert(method.ReturnType == typeof(bool), "not a bool result"); 520 Debug.Assert(expectedParameters.Length == 2, "not 2 parameters"); 521 522 // modify parameters (exactly 2, both need to be lifted) 523 ParameterInfo[] actualParameters = method.GetParameters(); 524 expectedParameters = new ParameterInfo[2]; 525 expectedParameters[0] = new SimpleParameterInfo(actualParameters[0]); 526 expectedParameters[1] = new SimpleParameterInfo(actualParameters[1]); 527 528 // set the result type 529 resultType = typeof(bool); 530 } 531 Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)532 public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) 533 { 534 // if either parameter is null, then result is false 535 if (parameters[0] == null) 536 return false; 537 if (parameters[1] == null) 538 return false; 539 540 // invoke the actual comparison (parameters are unwrapped) 541 return actualMethod.Invoke(null, invokeAttr, binder, parameters, culture); 542 } 543 } 544 545 internal class EnumOperationMethodInfo : MethodInfo 546 { 547 CodeBinaryOperatorType op; 548 ParameterInfo[] expectedParameters; 549 Type resultType; // may be nullable, enum, or value type 550 bool resultIsNullable; // true if resultType is nullable 551 552 Type lhsBaseType; // non-Nullable, may be enum 553 Type rhsBaseType; 554 Type resultBaseType; 555 556 Type lhsRootType; // underlying type (int, long, ushort, etc) 557 Type rhsRootType; 558 Type resultRootType; 559 560 [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] EnumOperationMethodInfo(Type lhs, CodeBinaryOperatorType operation, Type rhs, bool isZero)561 public EnumOperationMethodInfo(Type lhs, CodeBinaryOperatorType operation, Type rhs, bool isZero) 562 { 563 // only 5 arithmetic cases (U = underlying type of E): 564 // E = E + U 565 // E = U + E 566 // U = E - E 567 // E = E - U 568 // E = U - E 569 // plus 5 comparison cases 570 // E == E 571 // E < E 572 // E <= E 573 // E > E 574 // E >= E 575 // either E can be nullable 576 577 op = operation; 578 579 // parameters are easy -- they are the same as the type passed in 580 expectedParameters = new ParameterInfo[2]; 581 expectedParameters[0] = new SimpleParameterInfo(lhs); 582 expectedParameters[1] = new SimpleParameterInfo(rhs); 583 584 // compute return type (depends on type of operation) 585 // start by getting the types without Nullable<> 586 bool lhsNullable = ConditionHelper.IsNullableValueType(lhs); 587 bool rhsNullable = ConditionHelper.IsNullableValueType(rhs); 588 lhsBaseType = (lhsNullable) ? Nullable.GetUnderlyingType(lhs) : lhs; 589 rhsBaseType = (rhsNullable) ? Nullable.GetUnderlyingType(rhs) : rhs; 590 // determine the underlying types for both sides 591 if (lhsBaseType.IsEnum) 592 lhsRootType = EnumHelper.GetUnderlyingType(lhsBaseType); 593 else 594 lhsRootType = lhsBaseType; 595 596 if (rhsBaseType.IsEnum) 597 rhsRootType = EnumHelper.GetUnderlyingType(rhsBaseType); 598 else 599 rhsRootType = rhsBaseType; 600 601 switch (op) 602 { 603 case CodeBinaryOperatorType.Add: 604 // add always produces an enum, except enum + enum 605 if ((lhsBaseType.IsEnum) && (rhs.IsEnum)) 606 resultBaseType = lhsRootType; 607 else if (lhsBaseType.IsEnum) 608 resultBaseType = lhsBaseType; 609 else 610 resultBaseType = rhsBaseType; 611 // if either side is nullable, result is nullable 612 resultIsNullable = (lhsNullable || rhsNullable); 613 resultType = (resultIsNullable) ? typeof(Nullable<>).MakeGenericType(resultBaseType) : resultBaseType; 614 break; 615 case CodeBinaryOperatorType.Subtract: 616 // subtract can be an enum or the underlying type 617 if (rhsBaseType.IsEnum && lhsBaseType.IsEnum) 618 { 619 resultRootType = rhsRootType; 620 resultBaseType = rhsRootType; 621 } 622 else if (lhsBaseType.IsEnum) 623 { 624 // special case for E - 0 625 // if 0 is the underlying type, then use E - U 626 // if not the underlying type, then 0 becomes E, use E - E 627 resultRootType = lhsRootType; 628 if (isZero && rhsBaseType != lhsRootType) 629 resultBaseType = lhsRootType; 630 else 631 resultBaseType = lhsBaseType; 632 } 633 else // rhsType.IsEnum 634 { 635 // special case for 0 - E 636 // in all cases 0 becomes E, use E - E 637 resultRootType = rhsRootType; 638 if (isZero) 639 resultBaseType = rhsRootType; 640 else 641 resultBaseType = rhsBaseType; 642 } 643 resultIsNullable = (lhsNullable || rhsNullable); 644 resultType = (resultIsNullable) ? typeof(Nullable<>).MakeGenericType(resultBaseType) : resultBaseType; 645 break; 646 case CodeBinaryOperatorType.ValueEquality: 647 case CodeBinaryOperatorType.LessThan: 648 case CodeBinaryOperatorType.LessThanOrEqual: 649 case CodeBinaryOperatorType.GreaterThan: 650 case CodeBinaryOperatorType.GreaterThanOrEqual: 651 resultType = typeof(bool); 652 break; 653 } 654 } 655 GetBaseDefinition()656 public override MethodInfo GetBaseDefinition() 657 { 658 return null; 659 } 660 661 public override ICustomAttributeProvider ReturnTypeCustomAttributes 662 { 663 get { return null; } 664 } 665 666 public override MethodAttributes Attributes 667 { 668 get { return MethodAttributes.Static; } 669 } 670 GetMethodImplementationFlags()671 public override MethodImplAttributes GetMethodImplementationFlags() 672 { 673 return MethodImplAttributes.Runtime; 674 } 675 GetParameters()676 public override ParameterInfo[] GetParameters() 677 { 678 return expectedParameters; 679 } 680 681 [SuppressMessage("Microsoft.Performance", "CA1803:AvoidCostlyCallsWherePossible")] Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)682 public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) 683 { 684 // we should get passed in 2 values that correspond to the parameter types 685 686 object result; 687 ArithmeticLiteral leftArithmetic, rightArithmetic; 688 Literal leftLiteral, rightLiteral; 689 690 // for design-time types we couldn't find the underlying type, so do it now 691 if (lhsRootType == null) 692 lhsRootType = Enum.GetUnderlyingType(lhsBaseType); 693 if (rhsRootType == null) 694 rhsRootType = Enum.GetUnderlyingType(rhsBaseType); 695 696 switch (op) 697 { 698 case CodeBinaryOperatorType.Add: 699 // if either is null, then the result is null 700 if ((parameters[0] == null) || (parameters[1] == null)) 701 return null; 702 leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsRootType, parameters[0]); 703 rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsRootType, parameters[1]); 704 result = leftArithmetic.Add(rightArithmetic); 705 result = Executor.AdjustType(result.GetType(), result, resultBaseType); 706 if (resultIsNullable) 707 result = Activator.CreateInstance(resultType, result); 708 return result; 709 case CodeBinaryOperatorType.Subtract: 710 // if either is null, then the result is null 711 if ((parameters[0] == null) || (parameters[1] == null)) 712 return null; 713 leftArithmetic = ArithmeticLiteral.MakeLiteral(resultRootType, 714 Executor.AdjustType(lhsRootType, parameters[0], resultRootType)); 715 rightArithmetic = ArithmeticLiteral.MakeLiteral(resultRootType, 716 Executor.AdjustType(rhsRootType, parameters[1], resultRootType)); 717 result = leftArithmetic.Subtract(rightArithmetic); 718 result = Executor.AdjustType(result.GetType(), result, resultBaseType); 719 if (resultIsNullable) 720 result = Activator.CreateInstance(resultType, result); 721 return result; 722 723 case CodeBinaryOperatorType.ValueEquality: 724 leftLiteral = Literal.MakeLiteral(lhsRootType, parameters[0]); 725 rightLiteral = Literal.MakeLiteral(rhsRootType, parameters[1]); 726 return leftLiteral.Equal(rightLiteral); 727 case CodeBinaryOperatorType.LessThan: 728 leftLiteral = Literal.MakeLiteral(lhsRootType, parameters[0]); 729 rightLiteral = Literal.MakeLiteral(rhsRootType, parameters[1]); 730 return leftLiteral.LessThan(rightLiteral); 731 case CodeBinaryOperatorType.LessThanOrEqual: 732 leftLiteral = Literal.MakeLiteral(lhsRootType, parameters[0]); 733 rightLiteral = Literal.MakeLiteral(rhsRootType, parameters[1]); 734 return leftLiteral.LessThanOrEqual(rightLiteral); 735 case CodeBinaryOperatorType.GreaterThan: 736 leftLiteral = Literal.MakeLiteral(lhsRootType, parameters[0]); 737 rightLiteral = Literal.MakeLiteral(rhsRootType, parameters[1]); 738 return leftLiteral.GreaterThan(rightLiteral); 739 case CodeBinaryOperatorType.GreaterThanOrEqual: 740 leftLiteral = Literal.MakeLiteral(lhsRootType, parameters[0]); 741 rightLiteral = Literal.MakeLiteral(rhsRootType, parameters[1]); 742 return leftLiteral.GreaterThanOrEqual(rightLiteral); 743 } 744 string message = string.Format(CultureInfo.CurrentCulture, Messages.BinaryOpNotSupported, op.ToString()); 745 throw new RuleEvaluationException(message); 746 } 747 748 public override RuntimeMethodHandle MethodHandle 749 { 750 get { return new RuntimeMethodHandle(); } 751 } 752 753 public override Type DeclaringType 754 { 755 get { return typeof(Enum); } 756 } 757 GetCustomAttributes(Type attributeType, bool inherit)758 public override object[] GetCustomAttributes(Type attributeType, bool inherit) 759 { 760 return new object[0]; 761 } 762 GetCustomAttributes(bool inherit)763 public override object[] GetCustomAttributes(bool inherit) 764 { 765 return new object[0]; 766 } 767 IsDefined(Type attributeType, bool inherit)768 public override bool IsDefined(Type attributeType, bool inherit) 769 { 770 return true; 771 } 772 773 public override string Name 774 { 775 get { return "op_Enum"; } 776 } 777 778 public override Type ReflectedType 779 { 780 get { return resultType; } 781 } 782 783 public override Type ReturnType 784 { 785 get { return resultType; } 786 } 787 } 788 #endregion 789 790 #region SimpleRunTimeTypeProvider 791 792 internal class SimpleRunTimeTypeProvider : ITypeProvider 793 { 794 private Assembly root; 795 private List<Assembly> references; 796 SimpleRunTimeTypeProvider(Assembly startingAssembly)797 internal SimpleRunTimeTypeProvider(Assembly startingAssembly) 798 { 799 root = startingAssembly; 800 } 801 GetType(string name)802 public Type GetType(string name) 803 { 804 return GetType(name, false); 805 } 806 GetType(string name, bool throwOnError)807 public Type GetType(string name, bool throwOnError) 808 { 809 // is the type available in the main workflow assembly? 810 Type type = root.GetType(name, throwOnError, false); 811 if (type != null) 812 return type; 813 814 // now try mscorlib or this assembly 815 // (or if the name is an assembly qualified name) 816 type = Type.GetType(name, throwOnError, false); 817 if (type != null) 818 return type; 819 820 // no luck so far, so try all referenced assemblies 821 foreach (Assembly a in ReferencedAssemblies) 822 { 823 type = a.GetType(name, throwOnError, false); 824 if (type != null) 825 return type; 826 } 827 828 // keep going by trying all loaded assemblies 829 Assembly[] loaded = AppDomain.CurrentDomain.GetAssemblies(); 830 for (int i = 0; i < loaded.Length; ++i) 831 { 832 type = loaded[i].GetType(name, throwOnError, false); 833 if (type != null) 834 return type; 835 } 836 return null; 837 } 838 GetTypes()839 public Type[] GetTypes() 840 { 841 List<Type> types = new List<Type>(); 842 try 843 { 844 types.AddRange(root.GetTypes()); 845 } 846 catch (ReflectionTypeLoadException e) 847 { 848 // problems loading all the types, take what we can get 849 foreach (Type type in e.Types) 850 if (type != null) 851 types.Add(type); 852 } 853 foreach (Assembly a in ReferencedAssemblies) 854 { 855 try 856 { 857 types.AddRange(a.GetTypes()); 858 } 859 catch (ReflectionTypeLoadException e) 860 { 861 // problems loading all the types, take what we can get 862 foreach (Type type in e.Types) 863 if (type != null) 864 types.Add(type); 865 } 866 } 867 return types.ToArray(); 868 } 869 870 public Assembly LocalAssembly 871 { 872 get { return root; } 873 } 874 875 public ICollection<Assembly> ReferencedAssemblies 876 { 877 get 878 { 879 // references is created on demand, does not include root 880 if (references == null) 881 { 882 List<Assembly> list = new List<Assembly>(); 883 foreach (AssemblyName a in root.GetReferencedAssemblies()) 884 { 885 list.Add(Assembly.Load(a)); 886 } 887 references = list; 888 } 889 return references; 890 } 891 } 892 893 public IDictionary<object, Exception> TypeLoadErrors 894 { 895 get 896 { 897 // we never use this method, so add use of EventHandlers to keep compiler happy 898 TypesChanged.Invoke(this, null); 899 TypeLoadErrorsChanged.Invoke(this, null); 900 return null; 901 } 902 } 903 904 public event EventHandler TypesChanged; 905 906 public event EventHandler TypeLoadErrorsChanged; 907 } 908 #endregion 909 910 #region RuleValidation 911 912 public class RuleValidation 913 { 914 private Type thisType; 915 private ITypeProvider typeProvider; 916 private ValidationErrorCollection errors = new ValidationErrorCollection(); 917 private Dictionary<string, Type> typesUsed = new Dictionary<string, Type>(16); 918 private Dictionary<string, Type> typesUsedAuthorized; 919 private Stack<CodeExpression> activeParentNodes = new Stack<CodeExpression>(); 920 private Dictionary<CodeExpression, RuleExpressionInfo> expressionInfoMap = new Dictionary<CodeExpression, RuleExpressionInfo>(); 921 private Dictionary<CodeTypeReference, Type> typeRefMap = new Dictionary<CodeTypeReference, Type>(); 922 private bool checkStaticType; 923 private IList<AuthorizedType> authorizedTypes; 924 private static readonly Type voidType = typeof(void); 925 private static string voidTypeName = voidType.AssemblyQualifiedName; 926 927 #region Constructors 928 929 // Validate at design time. RuleValidation(Activity activity, ITypeProvider typeProvider, bool checkStaticType)930 public RuleValidation(Activity activity, ITypeProvider typeProvider, bool checkStaticType) 931 { 932 if (activity == null) 933 throw new ArgumentNullException("activity"); 934 if (typeProvider == null) 935 throw new ArgumentNullException("typeProvider"); 936 937 this.thisType = ConditionHelper.GetContextType(typeProvider, activity); 938 this.typeProvider = typeProvider; 939 this.checkStaticType = checkStaticType; 940 if (checkStaticType) 941 { 942 Debug.Assert(WorkflowCompilationContext.Current != null, "Can't have checkTypes set to true without a context in scope"); 943 this.authorizedTypes = WorkflowCompilationContext.Current.GetAuthorizedTypes(); 944 this.typesUsedAuthorized = new Dictionary<string, Type>(); 945 this.typesUsedAuthorized.Add(voidTypeName, voidType); 946 } 947 } 948 949 // Validate at runtime when we have the actual subject instance. This is 950 // mostly for conditions used in activities like IfElse. RuleValidation(object thisObject)951 internal RuleValidation(object thisObject) 952 { 953 if (thisObject == null) 954 throw new ArgumentNullException("thisObject"); 955 956 this.thisType = thisObject.GetType(); 957 this.typeProvider = new SimpleRunTimeTypeProvider(this.thisType.Assembly); 958 } 959 960 // Validate at runtime when we have just the type. This is mostly for rules. RuleValidation(Type thisType, ITypeProvider typeProvider)961 public RuleValidation(Type thisType, ITypeProvider typeProvider) 962 { 963 if (thisType == null) 964 throw new ArgumentNullException("thisType"); 965 966 this.thisType = thisType; 967 this.typeProvider = (typeProvider != null) ? typeProvider : new SimpleRunTimeTypeProvider(this.thisType.Assembly); 968 } 969 970 #endregion 971 972 #region Internal validation methods 973 ValidateConditionExpression(CodeExpression expression)974 internal bool ValidateConditionExpression(CodeExpression expression) 975 { 976 if (expression == null) 977 throw new ArgumentNullException("expression"); 978 979 // Run the validation pass. 980 RuleExpressionInfo exprInfo = RuleExpressionWalker.Validate(this, expression, false); 981 if (exprInfo == null) 982 return false; 983 984 Type resultType = exprInfo.ExpressionType; 985 if (!IsValidBooleanResult(resultType)) 986 { 987 // not a boolean, so complain unless another error may have caused this problem 988 if (resultType != null || Errors.Count == 0) 989 { 990 string message = Messages.ConditionMustBeBoolean; 991 ValidationError error = new ValidationError(message, ErrorNumbers.Error_ConditionMustBeBoolean); 992 error.UserData[RuleUserDataKeys.ErrorObject] = expression; 993 Errors.Add(error); 994 } 995 } 996 997 return Errors.Count == 0; 998 } 999 IsValidBooleanResult(Type type)1000 internal static bool IsValidBooleanResult(Type type) 1001 { 1002 return ((type == typeof(bool)) 1003 || (type == typeof(bool?)) 1004 || (ImplicitConversion(type, typeof(bool)))); 1005 } 1006 IsPrivate(MethodInfo methodInfo)1007 internal static bool IsPrivate(MethodInfo methodInfo) 1008 { 1009 return methodInfo.IsPrivate 1010 || methodInfo.IsFamily 1011 || methodInfo.IsFamilyOrAssembly 1012 || methodInfo.IsFamilyAndAssembly; 1013 } 1014 IsPrivate(FieldInfo fieldInfo)1015 internal static bool IsPrivate(FieldInfo fieldInfo) 1016 { 1017 return fieldInfo.IsPrivate 1018 || fieldInfo.IsFamily 1019 || fieldInfo.IsFamilyOrAssembly 1020 || fieldInfo.IsFamilyAndAssembly; 1021 } 1022 IsInternal(MethodInfo methodInfo)1023 internal static bool IsInternal(MethodInfo methodInfo) 1024 { 1025 return methodInfo.IsAssembly 1026 || methodInfo.IsFamilyAndAssembly; 1027 } 1028 IsInternal(FieldInfo fieldInfo)1029 internal static bool IsInternal(FieldInfo fieldInfo) 1030 { 1031 return fieldInfo.IsAssembly 1032 || fieldInfo.IsFamilyAndAssembly; 1033 } 1034 1035 #endregion 1036 1037 #region Miscellaneous public properties & methods 1038 1039 public Type ThisType 1040 { 1041 get { return thisType; } 1042 } 1043 GetTypeProvider()1044 internal ITypeProvider GetTypeProvider() 1045 { 1046 return typeProvider; 1047 } 1048 1049 public ValidationErrorCollection Errors 1050 { 1051 get { return errors; } 1052 } 1053 AllowInternalMembers(Type type)1054 internal bool AllowInternalMembers(Type type) 1055 { 1056 return type.Assembly == thisType.Assembly; 1057 } 1058 AddError(ValidationError error)1059 internal void AddError(ValidationError error) 1060 { 1061 this.Errors.Add(error); 1062 } 1063 PushParentExpression(CodeExpression newParent)1064 public bool PushParentExpression(CodeExpression newParent) 1065 { 1066 if (newParent == null) 1067 throw new ArgumentNullException("newParent"); 1068 1069 if (activeParentNodes.Contains(newParent)) 1070 { 1071 string message = string.Format(CultureInfo.CurrentCulture, Messages.CyclicalExpression); 1072 ValidationError error = new ValidationError(message, ErrorNumbers.Error_CyclicalExpression); 1073 error.UserData[RuleUserDataKeys.ErrorObject] = newParent; 1074 Errors.Add(error); 1075 return false; 1076 } 1077 1078 activeParentNodes.Push(newParent); 1079 return true; 1080 } 1081 PopParentExpression()1082 public void PopParentExpression() 1083 { 1084 activeParentNodes.Pop(); 1085 } 1086 1087 // Get the ExpressionInfo associated with a given CodeExpression ExpressionInfo(CodeExpression expression)1088 public RuleExpressionInfo ExpressionInfo(CodeExpression expression) 1089 { 1090 if (expression == null) 1091 throw new ArgumentNullException("expression"); 1092 1093 RuleExpressionInfo exprInfo = null; 1094 expressionInfoMap.TryGetValue(expression, out exprInfo); 1095 1096 return exprInfo; 1097 } 1098 1099 #endregion 1100 1101 #region CodeDom Expression Validation methods 1102 ValidateSubexpression(CodeExpression expr, RuleExpressionInternal ruleExpr, bool isWritten)1103 internal RuleExpressionInfo ValidateSubexpression(CodeExpression expr, RuleExpressionInternal ruleExpr, bool isWritten) 1104 { 1105 Debug.Assert(ruleExpr != null, "Validation::ValidateSubexpression - IRuleExpression is null"); 1106 Debug.Assert(expr != null, "Validation::ValidateSubexpression - CodeExpression is null"); 1107 1108 RuleExpressionInfo exprInfo = ruleExpr.Validate(expr, this, isWritten); 1109 1110 if (exprInfo != null) 1111 { 1112 // Add the CodeExpression object to the info map. We don't want to add the IRuleExpression guy 1113 // as the key, since it might likely be just a tearoff wrapper. 1114 expressionInfoMap[expr] = exprInfo; 1115 } 1116 1117 return exprInfo; 1118 } 1119 TypesAreAssignable(Type rhsType, Type lhsType, CodeExpression rhsExpression, out ValidationError error)1120 internal static bool TypesAreAssignable(Type rhsType, Type lhsType, CodeExpression rhsExpression, out ValidationError error) 1121 { 1122 // determine if rhsType can be implicitly converted to lhsType, 1123 // following the rules in C# specification section 6.1, 1124 // plus support for Nullable<T> 1125 1126 // all but 6.1.7 handled as a standard implicit conversion 1127 if (StandardImplicitConversion(rhsType, lhsType, rhsExpression, out error)) 1128 return true; 1129 if (error != null) 1130 return false; 1131 1132 // no standard implicit conversion works, see if user specified one 1133 // from section 6.4.3, start by determining what types to check 1134 // as we find each type, add the list of implicit conversions available 1135 if (FindImplicitConversion(rhsType, lhsType, out error) == null) 1136 return false; 1137 return true; 1138 } 1139 ExplicitConversionSpecified(Type fromType, Type toType, out ValidationError error)1140 internal static bool ExplicitConversionSpecified(Type fromType, Type toType, out ValidationError error) 1141 { 1142 // determine if fromType can be implicitly converted to toType, 1143 // following the rules in C# specification section 6.2 1144 1145 // start by seeing if there is a standard implicit conversion 1146 if (StandardImplicitConversion(fromType, toType, null, out error)) 1147 return true; 1148 if (error != null) 1149 return false; 1150 1151 // explicit numeric conversions 1152 // also handles Enum conversions, since GetTypeCode returns the underlying type 1153 if (fromType.IsValueType && toType.IsValueType && IsExplicitNumericConversion(fromType, toType)) 1154 return true; 1155 1156 // explicit reference conversions 1157 // this looks like the inverse of implicit conversions 1158 ValidationError dummyError; // so we don't return an error 1159 if (StandardImplicitConversion(toType, fromType, null, out dummyError)) 1160 return true; 1161 // include interface checks 1162 if (toType.IsInterface) 1163 { 1164 // from any class-type S to any interface-type T, provided S is not sealed and provided S does not implement T. 1165 // latter part should be handled by implicit conversion, so we are ok as long as class is not sealed 1166 if ((fromType.IsClass) && (!fromType.IsSealed)) 1167 return true; 1168 // from any interface-type S to any interface-type T, provided S is not derived from T. 1169 // again, if S derived from T, handled by implicit conversion above 1170 if (fromType.IsInterface) 1171 return true; 1172 } 1173 if (fromType.IsInterface) 1174 { 1175 // from any interface-type S to any class-type T, provided T is not sealed or provided T implements S. 1176 if ((toType.IsClass) && ((!toType.IsSealed) || (InterfaceMatch(toType.GetInterfaces(), fromType)))) 1177 return true; 1178 } 1179 1180 // no look for user-defined conversions 1181 // from section 6.4.4, start by determining what types to check 1182 // as we find each type, add the list of implicit conversions available 1183 if (FindExplicitConversion(fromType, toType, out error) == null) 1184 return false; 1185 return true; 1186 } 1187 InterfaceMatch(Type[] types, Type fromType)1188 private static bool InterfaceMatch(Type[] types, Type fromType) 1189 { 1190 foreach (Type t in types) 1191 { 1192 if (t == fromType) 1193 return true; 1194 } 1195 return false; 1196 } 1197 1198 [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] FindImplicitConversion(Type fromType, Type toType, out ValidationError error)1199 internal static MethodInfo FindImplicitConversion(Type fromType, Type toType, out ValidationError error) 1200 { 1201 List<MethodInfo> candidates = new List<MethodInfo>(); 1202 1203 bool fromIsNullable = ConditionHelper.IsNullableValueType(fromType); 1204 bool toIsNullable = ConditionHelper.IsNullableValueType(toType); 1205 Type fromType0 = (fromIsNullable) ? Nullable.GetUnderlyingType(fromType) : fromType; 1206 Type toType0 = (toIsNullable) ? Nullable.GetUnderlyingType(toType) : toType; 1207 1208 if (fromType0.IsClass) 1209 { 1210 AddImplicitConversions(fromType0, fromType, toType, candidates); 1211 Type baseType = fromType0.BaseType; 1212 while ((baseType != null) && (baseType != typeof(object))) 1213 { 1214 AddImplicitConversions(baseType, fromType, toType, candidates); 1215 baseType = baseType.BaseType; 1216 } 1217 } 1218 else if (IsStruct(fromType0)) 1219 { 1220 AddImplicitConversions(fromType0, fromType, toType, candidates); 1221 } 1222 if ((toType0.IsClass) || (IsStruct(toType0))) 1223 { 1224 AddImplicitConversions(toType0, fromType, toType, candidates); 1225 } 1226 1227 // if both types are nullable, add the lifted operators 1228 if (fromIsNullable && toIsNullable) 1229 { 1230 // start by finding all the conversion operators from S0 -> T0 1231 List<MethodInfo> liftedCandidates = new List<MethodInfo>(); 1232 if (fromType0.IsClass) 1233 { 1234 AddImplicitConversions(fromType0, fromType0, toType0, liftedCandidates); 1235 Type baseType = fromType0.BaseType; 1236 while ((baseType != null) && (baseType != typeof(object))) 1237 { 1238 AddImplicitConversions(baseType, fromType0, toType0, liftedCandidates); 1239 baseType = baseType.BaseType; 1240 } 1241 } 1242 else if (IsStruct(fromType0)) 1243 { 1244 AddImplicitConversions(fromType0, fromType0, toType0, liftedCandidates); 1245 } 1246 if ((toType0.IsClass) || (IsStruct(toType0))) 1247 { 1248 AddImplicitConversions(toType0, fromType0, toType0, liftedCandidates); 1249 } 1250 1251 // add them all to the candidates list as lifted methods (which wraps them appropriately) 1252 foreach (MethodInfo mi in liftedCandidates) 1253 { 1254 // only lift candidates that convert from a non-nullable value type 1255 // to a non-nullable value type 1256 ParameterInfo[] parameters = mi.GetParameters(); 1257 if (ConditionHelper.IsNonNullableValueType(mi.ReturnType) && ConditionHelper.IsNonNullableValueType(parameters[0].ParameterType)) 1258 candidates.Add(new LiftedConversionMethodInfo(mi)); 1259 } 1260 } 1261 1262 if (candidates.Count == 0) 1263 { 1264 // no overrides, so must be false 1265 string message = string.Format(CultureInfo.CurrentCulture, 1266 Messages.NoConversion, 1267 RuleDecompiler.DecompileType(fromType), 1268 RuleDecompiler.DecompileType(toType)); 1269 error = new ValidationError(message, ErrorNumbers.Error_OperandTypesIncompatible); 1270 return null; 1271 } 1272 1273 // find the most specific source type 1274 ValidationError dummyError; // so we don't return an error 1275 Type sx = candidates[0].GetParameters()[0].ParameterType; 1276 if (sx != fromType) 1277 { 1278 for (int i = 1; i < candidates.Count; ++i) 1279 { 1280 Type testType = candidates[i].GetParameters()[0].ParameterType; 1281 if (testType == fromType) 1282 { 1283 // we have a match with the source type, so that's the correct answer 1284 sx = fromType; 1285 break; 1286 } 1287 if (StandardImplicitConversion(testType, sx, null, out dummyError)) 1288 sx = testType; 1289 } 1290 } 1291 1292 // find the most specific target type 1293 Type tx = candidates[0].ReturnType; 1294 if (tx != toType) 1295 { 1296 for (int i = 1; i < candidates.Count; ++i) 1297 { 1298 Type testType = candidates[i].ReturnType; 1299 if (testType == toType) 1300 { 1301 // we have a match with the target type, so that's the correct answer 1302 tx = toType; 1303 break; 1304 } 1305 if (StandardImplicitConversion(tx, testType, null, out dummyError)) 1306 tx = testType; 1307 } 1308 } 1309 1310 // see how many candidates convert from sx to tx, ignoring lifted methods 1311 int numMatches = 0; 1312 int position = 0; 1313 for (int i = 0; i < candidates.Count; ++i) 1314 { 1315 if ((candidates[i].ReturnType == tx) && 1316 (candidates[i].GetParameters()[0].ParameterType == sx) && 1317 (!(candidates[i] is LiftedConversionMethodInfo))) 1318 { 1319 position = i; 1320 ++numMatches; 1321 } 1322 } 1323 if (numMatches == 1) 1324 { 1325 // found what we are looking for 1326 error = null; 1327 return candidates[position]; 1328 } 1329 1330 // now check for lifted conversions 1331 if ((toIsNullable) && (numMatches == 0)) 1332 { 1333 if (fromIsNullable) 1334 { 1335 for (int i = 0; i < candidates.Count; ++i) 1336 { 1337 if ((candidates[i].ReturnType == tx) && 1338 (candidates[i].GetParameters()[0].ParameterType == sx) && 1339 (candidates[i] is LiftedConversionMethodInfo)) 1340 { 1341 position = i; 1342 ++numMatches; 1343 } 1344 } 1345 if (numMatches == 1) 1346 { 1347 // found what we are looking for 1348 error = null; 1349 return candidates[position]; 1350 } 1351 } 1352 else 1353 { 1354 // we are doing a conversion T? = S, so a conversion from S -> T is valid 1355 MethodInfo result = FindImplicitConversion(fromType, toType0, out error); 1356 if (result != null) 1357 { 1358 error = null; 1359 // return it as a lifted method so the wrapping to T? is done 1360 return new LiftedConversionMethodInfo(result); 1361 } 1362 } 1363 } 1364 1365 // no exact matches, so it's an error 1366 string message2 = string.Format(CultureInfo.CurrentCulture, 1367 Messages.AmbiguousConversion, 1368 RuleDecompiler.DecompileType(fromType), 1369 RuleDecompiler.DecompileType(toType)); 1370 error = new ValidationError(message2, ErrorNumbers.Error_OperandTypesIncompatible); 1371 return null; 1372 } 1373 1374 [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] FindExplicitConversion(Type fromType, Type toType, out ValidationError error)1375 internal static MethodInfo FindExplicitConversion(Type fromType, Type toType, out ValidationError error) 1376 { 1377 List<MethodInfo> candidates = new List<MethodInfo>(); 1378 ValidationError dummyError; // don't return transient errors 1379 1380 bool fromIsNullable = ConditionHelper.IsNullableValueType(fromType); 1381 bool toIsNullable = ConditionHelper.IsNullableValueType(toType); 1382 Type fromType0 = (fromIsNullable) ? Nullable.GetUnderlyingType(fromType) : fromType; 1383 Type toType0 = (toIsNullable) ? Nullable.GetUnderlyingType(toType) : toType; 1384 1385 if (fromType0.IsClass) 1386 { 1387 AddExplicitConversions(fromType0, fromType, toType, candidates); 1388 Type baseType = fromType0.BaseType; 1389 while ((baseType != null) && (baseType != typeof(object))) 1390 { 1391 AddExplicitConversions(baseType, fromType, toType, candidates); 1392 baseType = baseType.BaseType; 1393 } 1394 } 1395 else if (IsStruct(fromType0)) 1396 { 1397 AddExplicitConversions(fromType0, fromType, toType, candidates); 1398 } 1399 if (toType0.IsClass) 1400 { 1401 AddExplicitConversions(toType0, fromType, toType, candidates); 1402 Type baseType = toType0.BaseType; 1403 while ((baseType != null) && (baseType != typeof(object))) 1404 { 1405 AddExplicitConversions(baseType, fromType, toType, candidates); 1406 baseType = baseType.BaseType; 1407 } 1408 } 1409 else if (IsStruct(toType0)) 1410 { 1411 AddExplicitConversions(toType0, fromType, toType, candidates); 1412 } 1413 1414 // if both types are nullable, add the lifted operators 1415 if (fromIsNullable && toIsNullable) 1416 { 1417 // start by finding all the conversion operators from S0 -> T0 1418 List<MethodInfo> liftedCandidates = new List<MethodInfo>(); 1419 if (fromType0.IsClass) 1420 { 1421 AddExplicitConversions(fromType0, fromType0, toType0, liftedCandidates); 1422 Type baseType = fromType0.BaseType; 1423 while ((baseType != null) && (baseType != typeof(object))) 1424 { 1425 AddExplicitConversions(baseType, fromType0, toType0, liftedCandidates); 1426 baseType = baseType.BaseType; 1427 } 1428 } 1429 else if (IsStruct(fromType0)) 1430 { 1431 AddExplicitConversions(fromType0, fromType0, toType0, liftedCandidates); 1432 } 1433 if (toType0.IsClass) 1434 { 1435 AddExplicitConversions(toType0, fromType0, toType0, liftedCandidates); 1436 Type baseType = toType0.BaseType; 1437 while ((baseType != null) && (baseType != typeof(object))) 1438 { 1439 AddExplicitConversions(baseType, fromType0, toType0, liftedCandidates); 1440 baseType = baseType.BaseType; 1441 } 1442 } 1443 else if (IsStruct(toType0)) 1444 { 1445 AddExplicitConversions(toType0, fromType0, toType0, liftedCandidates); 1446 } 1447 1448 // add them all to the candidates list as lifted methods (which wraps them appropriately) 1449 foreach (MethodInfo mi in liftedCandidates) 1450 candidates.Add(new LiftedConversionMethodInfo(mi)); 1451 } 1452 1453 if (candidates.Count == 0) 1454 { 1455 // no overrides, so must be false 1456 string message = string.Format(CultureInfo.CurrentCulture, 1457 Messages.NoConversion, 1458 RuleDecompiler.DecompileType(fromType), 1459 RuleDecompiler.DecompileType(toType)); 1460 error = new ValidationError(message, ErrorNumbers.Error_OperandTypesIncompatible); 1461 return null; 1462 } 1463 1464 // find the most specific source type 1465 // if any are s, s is the answer 1466 Type sx = null; 1467 for (int i = 0; i < candidates.Count; ++i) 1468 { 1469 Type testType = candidates[i].GetParameters()[0].ParameterType; 1470 if (testType == fromType) 1471 { 1472 // we have a match with the source type, so that's the correct answer 1473 sx = fromType; 1474 break; 1475 } 1476 } 1477 // if no match, find the most encompassed type if the type encompasses s 1478 if (sx == null) 1479 { 1480 for (int i = 0; i < candidates.Count; ++i) 1481 { 1482 Type testType = candidates[i].GetParameters()[0].ParameterType; 1483 if (StandardImplicitConversion(fromType, testType, null, out dummyError)) 1484 { 1485 if (sx == null) 1486 sx = testType; 1487 else if (StandardImplicitConversion(testType, sx, null, out dummyError)) 1488 sx = testType; 1489 } 1490 } 1491 } 1492 // still no match, find most encompassing type 1493 if (sx == null) 1494 { 1495 for (int i = 0; i < candidates.Count; ++i) 1496 { 1497 Type testType = candidates[i].GetParameters()[0].ParameterType; 1498 if (StandardImplicitConversion(testType, fromType, null, out dummyError)) 1499 { 1500 if (sx == null) 1501 sx = testType; 1502 else if (StandardImplicitConversion(sx, testType, null, out dummyError)) 1503 sx = testType; 1504 } 1505 } 1506 } 1507 1508 // find the most specific target type 1509 // if any are t, t is the answer 1510 Type tx = null; 1511 for (int i = 0; i < candidates.Count; ++i) 1512 { 1513 Type testType = candidates[i].ReturnType; 1514 if (testType == toType) 1515 { 1516 // we have a match with the target type, so that's the correct answer 1517 tx = toType; 1518 break; 1519 } 1520 } 1521 // if no match, find the most encompassed type if the type encompasses s 1522 if (tx == null) 1523 { 1524 for (int i = 0; i < candidates.Count; ++i) 1525 { 1526 Type testType = candidates[i].ReturnType; 1527 if (StandardImplicitConversion(testType, toType, null, out dummyError)) 1528 { 1529 if (tx == null) 1530 tx = testType; 1531 else if (StandardImplicitConversion(tx, testType, null, out dummyError)) 1532 tx = testType; 1533 } 1534 } 1535 } 1536 // still no match, find most encompassing type 1537 if (tx == null) 1538 { 1539 for (int i = 0; i < candidates.Count; ++i) 1540 { 1541 Type testType = candidates[i].ReturnType; 1542 if (StandardImplicitConversion(toType, testType, null, out dummyError)) 1543 { 1544 if (tx == null) 1545 tx = testType; 1546 else if (StandardImplicitConversion(testType, tx, null, out dummyError)) 1547 tx = testType; 1548 } 1549 } 1550 } 1551 1552 // see how many candidates convert from sx to tx, ignoring lifted methods 1553 int numMatches = 0; 1554 int position = 0; 1555 for (int i = 0; i < candidates.Count; ++i) 1556 { 1557 if ((candidates[i].ReturnType == tx) && 1558 (candidates[i].GetParameters()[0].ParameterType == sx) && 1559 (!(candidates[i] is LiftedConversionMethodInfo))) 1560 { 1561 position = i; 1562 ++numMatches; 1563 } 1564 } 1565 if (numMatches == 1) 1566 { 1567 // found what we are looking for 1568 error = null; 1569 return candidates[position]; 1570 } 1571 1572 // now check for lifted conversions 1573 if ((toIsNullable) && (numMatches == 0)) 1574 { 1575 if (fromIsNullable) 1576 { 1577 for (int i = 0; i < candidates.Count; ++i) 1578 { 1579 if ((candidates[i].ReturnType == tx) && 1580 (candidates[i].GetParameters()[0].ParameterType == sx) && 1581 (candidates[i] is LiftedConversionMethodInfo)) 1582 { 1583 position = i; 1584 ++numMatches; 1585 } 1586 } 1587 if (numMatches == 1) 1588 { 1589 // found what we are looking for 1590 error = null; 1591 return candidates[position]; 1592 } 1593 } 1594 else 1595 { 1596 // we are doing a conversion T? = S, so a conversion from S -> T is valid 1597 MethodInfo result = FindExplicitConversion(fromType, toType0, out error); 1598 if (result != null) 1599 { 1600 error = null; 1601 // return it as a lifted method so the wrapping to T? is done 1602 return new LiftedConversionMethodInfo(result); 1603 } 1604 } 1605 } 1606 1607 // no exact matches, so it's an error 1608 string message2 = string.Format(CultureInfo.CurrentCulture, 1609 Messages.AmbiguousConversion, 1610 RuleDecompiler.DecompileType(fromType), 1611 RuleDecompiler.DecompileType(toType)); 1612 error = new ValidationError(message2, ErrorNumbers.Error_OperandTypesIncompatible); 1613 return null; 1614 } 1615 IsStruct(Type type)1616 private static bool IsStruct(Type type) 1617 { 1618 return ((type.IsValueType) && (!type.IsPrimitive)); 1619 } 1620 1621 [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] IsExplicitNumericConversion(Type sourceType, Type testType)1622 private static bool IsExplicitNumericConversion(Type sourceType, Type testType) 1623 { 1624 // includes the implicit conversions as well 1625 1626 // unwrap nullables 1627 TypeCode sourceTypeCode = (ConditionHelper.IsNullableValueType(sourceType)) 1628 ? Type.GetTypeCode(sourceType.GetGenericArguments()[0]) 1629 : Type.GetTypeCode(sourceType); 1630 TypeCode testTypeCode = (ConditionHelper.IsNullableValueType(testType)) 1631 ? Type.GetTypeCode(testType.GetGenericArguments()[0]) 1632 : Type.GetTypeCode(testType); 1633 1634 switch (sourceTypeCode) 1635 { 1636 case TypeCode.SByte: 1637 switch (testTypeCode) 1638 { 1639 case TypeCode.SByte: 1640 case TypeCode.Byte: 1641 case TypeCode.UInt16: 1642 case TypeCode.UInt32: 1643 case TypeCode.UInt64: 1644 case TypeCode.Char: 1645 1646 case TypeCode.Int16: 1647 case TypeCode.Int32: 1648 case TypeCode.Int64: 1649 case TypeCode.Single: 1650 case TypeCode.Double: 1651 case TypeCode.Decimal: 1652 return true; 1653 } 1654 return false; 1655 1656 case TypeCode.Byte: 1657 switch (testTypeCode) 1658 { 1659 case TypeCode.Byte: 1660 case TypeCode.SByte: 1661 case TypeCode.Char: 1662 1663 case TypeCode.Int16: 1664 case TypeCode.UInt16: 1665 case TypeCode.Int32: 1666 case TypeCode.UInt32: 1667 case TypeCode.Int64: 1668 case TypeCode.UInt64: 1669 case TypeCode.Single: 1670 case TypeCode.Double: 1671 case TypeCode.Decimal: 1672 return true; 1673 } 1674 return false; 1675 1676 case TypeCode.Int16: 1677 switch (testTypeCode) 1678 { 1679 case TypeCode.SByte: 1680 case TypeCode.Byte: 1681 case TypeCode.UInt16: 1682 case TypeCode.UInt32: 1683 case TypeCode.UInt64: 1684 case TypeCode.Char: 1685 1686 case TypeCode.Int16: 1687 case TypeCode.Int32: 1688 case TypeCode.Int64: 1689 case TypeCode.Single: 1690 case TypeCode.Double: 1691 case TypeCode.Decimal: 1692 return true; 1693 } 1694 return false; 1695 1696 case TypeCode.UInt16: 1697 switch (testTypeCode) 1698 { 1699 case TypeCode.SByte: 1700 case TypeCode.Byte: 1701 case TypeCode.Int16: 1702 case TypeCode.UInt16: 1703 case TypeCode.Char: 1704 1705 case TypeCode.Int32: 1706 case TypeCode.UInt32: 1707 case TypeCode.Int64: 1708 case TypeCode.UInt64: 1709 case TypeCode.Single: 1710 case TypeCode.Double: 1711 case TypeCode.Decimal: 1712 return true; 1713 } 1714 return false; 1715 1716 case TypeCode.Int32: 1717 switch (testTypeCode) 1718 { 1719 case TypeCode.SByte: 1720 case TypeCode.Byte: 1721 case TypeCode.Int16: 1722 case TypeCode.UInt16: 1723 case TypeCode.Int32: 1724 case TypeCode.UInt32: 1725 case TypeCode.UInt64: 1726 case TypeCode.Char: 1727 1728 case TypeCode.Int64: 1729 case TypeCode.Single: 1730 case TypeCode.Double: 1731 case TypeCode.Decimal: 1732 return true; 1733 } 1734 return false; 1735 1736 case TypeCode.UInt32: 1737 switch (testTypeCode) 1738 { 1739 case TypeCode.SByte: 1740 case TypeCode.Byte: 1741 case TypeCode.Int16: 1742 case TypeCode.UInt16: 1743 case TypeCode.Int32: 1744 case TypeCode.UInt32: 1745 case TypeCode.Char: 1746 1747 case TypeCode.Int64: 1748 case TypeCode.UInt64: 1749 case TypeCode.Single: 1750 case TypeCode.Double: 1751 case TypeCode.Decimal: 1752 return true; 1753 } 1754 return false; 1755 1756 case TypeCode.Int64: 1757 switch (testTypeCode) 1758 { 1759 case TypeCode.SByte: 1760 case TypeCode.Byte: 1761 case TypeCode.Int16: 1762 case TypeCode.UInt16: 1763 case TypeCode.Int32: 1764 case TypeCode.UInt32: 1765 case TypeCode.Int64: 1766 case TypeCode.UInt64: 1767 case TypeCode.Char: 1768 1769 case TypeCode.Single: 1770 case TypeCode.Double: 1771 case TypeCode.Decimal: 1772 return true; 1773 } 1774 return false; 1775 1776 case TypeCode.UInt64: 1777 switch (testTypeCode) 1778 { 1779 case TypeCode.SByte: 1780 case TypeCode.Byte: 1781 case TypeCode.Int16: 1782 case TypeCode.UInt16: 1783 case TypeCode.Int32: 1784 case TypeCode.UInt32: 1785 case TypeCode.Int64: 1786 case TypeCode.UInt64: 1787 case TypeCode.Char: 1788 1789 case TypeCode.Single: 1790 case TypeCode.Double: 1791 case TypeCode.Decimal: 1792 return true; 1793 } 1794 return false; 1795 1796 case TypeCode.Char: 1797 switch (testTypeCode) 1798 { 1799 case TypeCode.Char: 1800 case TypeCode.SByte: 1801 case TypeCode.Byte: 1802 case TypeCode.Int16: 1803 1804 case TypeCode.UInt16: 1805 case TypeCode.Int32: 1806 case TypeCode.UInt32: 1807 case TypeCode.Int64: 1808 case TypeCode.UInt64: 1809 case TypeCode.Single: 1810 case TypeCode.Double: 1811 case TypeCode.Decimal: 1812 return true; 1813 } 1814 return false; 1815 1816 case TypeCode.Single: 1817 switch (testTypeCode) 1818 { 1819 case TypeCode.SByte: 1820 case TypeCode.Byte: 1821 case TypeCode.Int16: 1822 case TypeCode.UInt16: 1823 case TypeCode.Int32: 1824 case TypeCode.UInt32: 1825 case TypeCode.Int64: 1826 case TypeCode.UInt64: 1827 case TypeCode.Char: 1828 case TypeCode.Single: 1829 case TypeCode.Decimal: 1830 1831 case TypeCode.Double: 1832 return true; 1833 } 1834 return false; 1835 1836 case TypeCode.Double: 1837 switch (testTypeCode) 1838 { 1839 case TypeCode.SByte: 1840 case TypeCode.Byte: 1841 case TypeCode.Int16: 1842 case TypeCode.UInt16: 1843 case TypeCode.Int32: 1844 case TypeCode.UInt32: 1845 case TypeCode.Int64: 1846 case TypeCode.UInt64: 1847 case TypeCode.Char: 1848 case TypeCode.Single: 1849 case TypeCode.Double: 1850 case TypeCode.Decimal: 1851 return true; 1852 } 1853 return false; 1854 1855 case TypeCode.Decimal: 1856 switch (testTypeCode) 1857 { 1858 case TypeCode.SByte: 1859 case TypeCode.Byte: 1860 case TypeCode.Int16: 1861 case TypeCode.UInt16: 1862 case TypeCode.Int32: 1863 case TypeCode.UInt32: 1864 case TypeCode.Int64: 1865 case TypeCode.UInt64: 1866 case TypeCode.Char: 1867 case TypeCode.Single: 1868 case TypeCode.Double: 1869 case TypeCode.Decimal: 1870 return true; 1871 } 1872 return false; 1873 } 1874 return false; 1875 } 1876 1877 ImplicitConversion(Type fromType, Type toType)1878 internal static bool ImplicitConversion(Type fromType, Type toType) 1879 { 1880 ValidationError error; 1881 1882 // is there a standard conversion we can use 1883 if (StandardImplicitConversion(fromType, toType, null, out error)) 1884 return true; 1885 1886 // no standard one, did the user provide one? 1887 return (FindImplicitConversion(fromType, toType, out error) != null); 1888 } 1889 1890 [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] StandardImplicitConversion(Type rhsType, Type lhsType, CodeExpression rhsExpression, out ValidationError error)1891 internal static bool StandardImplicitConversion(Type rhsType, Type lhsType, CodeExpression rhsExpression, out ValidationError error) 1892 { 1893 error = null; 1894 1895 // 6.1.1 identity conversion 1896 if (rhsType == lhsType) 1897 { 1898 // Easy special case... they're the same type. 1899 return true; 1900 } 1901 1902 // 6.1.4 (h) from the null type to any reference-type 1903 if (rhsType == typeof(NullLiteral)) 1904 { 1905 // Special case if the RHS is 'null'; just make sure the LHS type can be assigned a null value. 1906 if (ConditionHelper.IsNonNullableValueType(lhsType)) 1907 { 1908 string message = string.Format(CultureInfo.CurrentCulture, Messages.AssignNotAllowed, Messages.NullValue, RuleDecompiler.DecompileType(lhsType)); 1909 error = new ValidationError(message, ErrorNumbers.Error_OperandTypesIncompatible); 1910 return false; 1911 } 1912 return true; 1913 } 1914 1915 // check for nullables 1916 bool lhsIsNullable = ConditionHelper.IsNullableValueType(lhsType); 1917 bool rhsIsNullable = ConditionHelper.IsNullableValueType(rhsType); 1918 if (rhsIsNullable) 1919 { 1920 if (!lhsIsNullable) 1921 { 1922 // We had T1 = T2?, which is not valid for any T1 or T2, unless T1 is object 1923 return (lhsType == typeof(object)); 1924 } 1925 1926 rhsType = Nullable.GetUnderlyingType(rhsType); 1927 } 1928 1929 if (lhsIsNullable) 1930 lhsType = Nullable.GetUnderlyingType(lhsType); 1931 1932 if (lhsType == rhsType) 1933 { 1934 // We had T? = T, which is valid. 1935 return true; 1936 } 1937 1938 // handle rest of 6.1.4 1939 if (TypeProvider.IsAssignable(lhsType, rhsType)) 1940 { 1941 // They are assignable, which will handle inheritance and trivial up-casting. 1942 return true; 1943 } 1944 1945 // 6.1.3 implicit enumeration conversions 1946 if (lhsType.IsEnum) 1947 { 1948 // right-hand side can be decimal-integer-literal 0 1949 CodePrimitiveExpression primitive = rhsExpression as CodePrimitiveExpression; 1950 if ((primitive == null) || (primitive.Value == null)) 1951 { 1952 // not a constant 1953 return false; 1954 } 1955 switch (Type.GetTypeCode(primitive.Value.GetType())) 1956 { 1957 case TypeCode.SByte: 1958 return ((sbyte)primitive.Value == 0); 1959 case TypeCode.Byte: 1960 return ((byte)primitive.Value == 0); 1961 case TypeCode.Int16: 1962 return ((short)primitive.Value == 0); 1963 case TypeCode.UInt16: 1964 return ((ushort)primitive.Value == 0); 1965 case TypeCode.Int32: 1966 return ((int)primitive.Value == 0); 1967 case TypeCode.UInt32: 1968 return ((uint)primitive.Value == 0); 1969 case TypeCode.Int64: 1970 return ((long)primitive.Value == 0); 1971 case TypeCode.UInt64: 1972 return ((ulong)primitive.Value == 0); 1973 case TypeCode.Char: 1974 return ((char)primitive.Value == 0); 1975 } 1976 return false; 1977 } 1978 if (rhsType.IsEnum) 1979 { 1980 // don't treat enums as numbers 1981 return false; 1982 } 1983 1984 // 6.1.2 implicit numeric conversions 1985 // 6.1.6 implicit constant expression conversions 1986 // not assignable, but the assignment might still be valid for 1987 // value types if a free conversion is available. 1988 TypeCode lhsTypeCode = Type.GetTypeCode(lhsType); 1989 TypeCode rhsTypeCode = Type.GetTypeCode(rhsType); 1990 1991 switch (lhsTypeCode) 1992 { 1993 case TypeCode.Decimal: 1994 switch (rhsTypeCode) 1995 { 1996 case TypeCode.SByte: 1997 case TypeCode.Byte: 1998 case TypeCode.Int16: 1999 case TypeCode.UInt16: 2000 case TypeCode.Int32: 2001 case TypeCode.UInt32: 2002 case TypeCode.Int64: 2003 case TypeCode.UInt64: 2004 case TypeCode.Decimal: 2005 case TypeCode.Char: 2006 return true; 2007 } 2008 return false; 2009 2010 case TypeCode.Double: 2011 switch (rhsTypeCode) 2012 { 2013 case TypeCode.SByte: 2014 case TypeCode.Byte: 2015 case TypeCode.Int16: 2016 case TypeCode.UInt16: 2017 case TypeCode.Int32: 2018 case TypeCode.UInt32: 2019 case TypeCode.Int64: 2020 case TypeCode.UInt64: 2021 case TypeCode.Single: 2022 case TypeCode.Double: 2023 case TypeCode.Char: 2024 return true; 2025 } 2026 return false; 2027 2028 case TypeCode.Single: 2029 switch (rhsTypeCode) 2030 { 2031 case TypeCode.SByte: 2032 case TypeCode.Byte: 2033 case TypeCode.Int16: 2034 case TypeCode.UInt16: 2035 case TypeCode.Int32: 2036 case TypeCode.UInt32: 2037 case TypeCode.Int64: 2038 case TypeCode.UInt64: 2039 case TypeCode.Single: 2040 case TypeCode.Char: 2041 return true; 2042 } 2043 return false; 2044 2045 case TypeCode.Char: 2046 switch (rhsTypeCode) 2047 { 2048 case TypeCode.Char: 2049 return true; 2050 case TypeCode.SByte: 2051 case TypeCode.Byte: 2052 case TypeCode.Int16: 2053 case TypeCode.UInt16: 2054 case TypeCode.Int32: 2055 case TypeCode.UInt32: 2056 case TypeCode.Int64: 2057 case TypeCode.UInt64: 2058 // Maybe, if the value is in range. 2059 return CheckValueRange(rhsExpression, lhsType, out error); 2060 } 2061 return false; 2062 2063 case TypeCode.SByte: 2064 switch (rhsTypeCode) 2065 { 2066 case TypeCode.SByte: 2067 return true; 2068 case TypeCode.Byte: 2069 case TypeCode.Int16: 2070 case TypeCode.UInt16: 2071 case TypeCode.Int32: 2072 case TypeCode.UInt32: 2073 case TypeCode.Int64: 2074 case TypeCode.UInt64: 2075 case TypeCode.Char: 2076 // Maybe, if the value is in range. 2077 return CheckValueRange(rhsExpression, lhsType, out error); 2078 } 2079 return false; 2080 2081 case TypeCode.Byte: 2082 switch (rhsTypeCode) 2083 { 2084 case TypeCode.Byte: 2085 return true; 2086 case TypeCode.SByte: 2087 case TypeCode.Int16: 2088 case TypeCode.UInt16: 2089 case TypeCode.Int32: 2090 case TypeCode.UInt32: 2091 case TypeCode.Int64: 2092 case TypeCode.UInt64: 2093 case TypeCode.Char: 2094 // Maybe, if the value is in range. 2095 return CheckValueRange(rhsExpression, lhsType, out error); 2096 } 2097 return false; 2098 2099 case TypeCode.Int16: 2100 switch (rhsTypeCode) 2101 { 2102 case TypeCode.SByte: 2103 case TypeCode.Byte: 2104 case TypeCode.Int16: 2105 return true; 2106 case TypeCode.UInt16: 2107 case TypeCode.Int32: 2108 case TypeCode.UInt32: 2109 case TypeCode.Int64: 2110 case TypeCode.UInt64: 2111 case TypeCode.Char: 2112 // Maybe, if the value is in range. 2113 return CheckValueRange(rhsExpression, lhsType, out error); 2114 } 2115 return false; 2116 2117 case TypeCode.Int32: 2118 switch (rhsTypeCode) 2119 { 2120 case TypeCode.SByte: 2121 case TypeCode.Byte: 2122 case TypeCode.Int16: 2123 case TypeCode.UInt16: 2124 case TypeCode.Int32: 2125 case TypeCode.Char: 2126 return true; 2127 case TypeCode.UInt32: 2128 case TypeCode.Int64: 2129 case TypeCode.UInt64: 2130 // Maybe, if the value is in range. 2131 return CheckValueRange(rhsExpression, lhsType, out error); 2132 } 2133 return false; 2134 2135 case TypeCode.Int64: 2136 switch (rhsTypeCode) 2137 { 2138 case TypeCode.SByte: 2139 case TypeCode.Byte: 2140 case TypeCode.Int16: 2141 case TypeCode.UInt16: 2142 case TypeCode.Int32: 2143 case TypeCode.UInt32: 2144 case TypeCode.Int64: 2145 case TypeCode.Char: 2146 return true; 2147 case TypeCode.UInt64: 2148 // Maybe, if the value is in range. 2149 return CheckValueRange(rhsExpression, lhsType, out error); 2150 } 2151 return false; 2152 2153 case TypeCode.UInt16: 2154 switch (rhsTypeCode) 2155 { 2156 case TypeCode.Byte: 2157 case TypeCode.UInt16: 2158 case TypeCode.Char: 2159 return true; 2160 case TypeCode.SByte: 2161 case TypeCode.Int16: 2162 case TypeCode.Int32: 2163 case TypeCode.UInt32: 2164 case TypeCode.Int64: 2165 case TypeCode.UInt64: 2166 // Maybe, if the value is in range. 2167 return CheckValueRange(rhsExpression, lhsType, out error); 2168 } 2169 return false; 2170 2171 case TypeCode.UInt32: 2172 switch (rhsTypeCode) 2173 { 2174 case TypeCode.Byte: 2175 case TypeCode.UInt16: 2176 case TypeCode.UInt32: 2177 case TypeCode.Char: 2178 return true; 2179 case TypeCode.SByte: 2180 case TypeCode.Int16: 2181 case TypeCode.Int32: 2182 case TypeCode.Int64: 2183 case TypeCode.UInt64: 2184 // Maybe, if the value is in range. 2185 return CheckValueRange(rhsExpression, lhsType, out error); 2186 } 2187 return false; 2188 2189 case TypeCode.UInt64: 2190 switch (rhsTypeCode) 2191 { 2192 case TypeCode.Byte: 2193 case TypeCode.UInt16: 2194 case TypeCode.UInt32: 2195 case TypeCode.UInt64: 2196 case TypeCode.Char: 2197 return true; 2198 case TypeCode.SByte: 2199 case TypeCode.Int16: 2200 case TypeCode.Int32: 2201 case TypeCode.Int64: 2202 // Maybe, if the value is in range. 2203 return CheckValueRange(rhsExpression, lhsType, out error); 2204 } 2205 return false; 2206 2207 default: 2208 // It wasn't a numeric type, it was some other kind of value type (e.g., bool, 2209 // DateTime, etc). There will be no conversions. 2210 return false; 2211 } 2212 } 2213 AddImplicitConversions(Type t, Type source, Type target, List<MethodInfo> methods)2214 private static void AddImplicitConversions(Type t, Type source, Type target, List<MethodInfo> methods) 2215 { 2216 // append the list of methods that match the name specified 2217 // s is the source type, so the parameter must encompass it 2218 // t is the target type, so it must encompass the result 2219 MethodInfo[] possible = t.GetMethods(BindingFlags.Static | BindingFlags.Public); 2220 foreach (MethodInfo mi in possible) 2221 { 2222 if ((mi.Name == "op_Implicit") && (mi.GetParameters().Length == 1)) 2223 { 2224 Type sourceType = mi.GetParameters()[0].ParameterType; 2225 Type targetType = mi.ReturnType; 2226 ValidationError error; 2227 if (StandardImplicitConversion(source, sourceType, null, out error) && 2228 StandardImplicitConversion(targetType, target, null, out error)) 2229 { 2230 if (!methods.Contains(mi)) 2231 methods.Add(mi); 2232 } 2233 } 2234 } 2235 } 2236 AddExplicitConversions(Type t, Type source, Type target, List<MethodInfo> methods)2237 private static void AddExplicitConversions(Type t, Type source, Type target, List<MethodInfo> methods) 2238 { 2239 // append the list of methods that match the name specified 2240 // s is the source type, so the parameter must encompass it 2241 // t is the target type, so it must encompass the result 2242 MethodInfo[] possible = t.GetMethods(BindingFlags.Static | BindingFlags.Public); 2243 foreach (MethodInfo mi in possible) 2244 { 2245 if (((mi.Name == "op_Implicit") || (mi.Name == "op_Explicit")) && (mi.GetParameters().Length == 1)) 2246 { 2247 Type sourceType = mi.GetParameters()[0].ParameterType; 2248 Type targetType = mi.ReturnType; 2249 ValidationError error; 2250 if ((StandardImplicitConversion(source, sourceType, null, out error) || StandardImplicitConversion(sourceType, source, null, out error)) 2251 && (StandardImplicitConversion(target, targetType, null, out error) || StandardImplicitConversion(targetType, target, null, out error))) 2252 { 2253 if (!methods.Contains(mi)) 2254 methods.Add(mi); 2255 } 2256 } 2257 } 2258 } 2259 2260 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] CheckValueRange(CodeExpression rhsExpression, Type lhsType, out ValidationError error)2261 private static bool CheckValueRange(CodeExpression rhsExpression, Type lhsType, out ValidationError error) 2262 { 2263 error = null; 2264 2265 CodePrimitiveExpression primitive = rhsExpression as CodePrimitiveExpression; 2266 if (primitive != null) 2267 { 2268 try 2269 { 2270 System.Convert.ChangeType(primitive.Value, lhsType, CultureInfo.CurrentCulture); 2271 // If we get here without throwing, it's valid. 2272 return true; 2273 } 2274 catch (Exception e) 2275 { 2276 error = new ValidationError(e.Message, ErrorNumbers.Error_OperandTypesIncompatible); 2277 return false; 2278 } 2279 } 2280 2281 return false; 2282 } 2283 ValidateMemberAccess( CodeExpression targetExpression, Type targetType, FieldInfo accessorMethod, string memberName, CodeExpression parentExpr)2284 internal bool ValidateMemberAccess( 2285 CodeExpression targetExpression, Type targetType, FieldInfo accessorMethod, string memberName, CodeExpression parentExpr) 2286 { 2287 return this.ValidateMemberAccess( 2288 targetExpression, targetType, memberName, parentExpr, 2289 accessorMethod.DeclaringType.Assembly, RuleValidation.IsPrivate(accessorMethod), RuleValidation.IsInternal(accessorMethod), accessorMethod.IsStatic); 2290 } 2291 ValidateMemberAccess( CodeExpression targetExpression, Type targetType, MethodInfo accessorMethod, string memberName, CodeExpression parentExpr)2292 internal bool ValidateMemberAccess( 2293 CodeExpression targetExpression, Type targetType, MethodInfo accessorMethod, string memberName, CodeExpression parentExpr) 2294 { 2295 return this.ValidateMemberAccess( 2296 targetExpression, targetType, memberName, parentExpr, 2297 accessorMethod.DeclaringType.Assembly, RuleValidation.IsPrivate(accessorMethod), RuleValidation.IsInternal(accessorMethod), accessorMethod.IsStatic); 2298 } 2299 ValidateMemberAccess( CodeExpression targetExpression, Type targetType, string memberName, CodeExpression parentExpr, Assembly methodAssembly, bool isPrivate, bool isInternal, bool isStatic)2300 private bool ValidateMemberAccess( 2301 CodeExpression targetExpression, Type targetType, string memberName, CodeExpression parentExpr, 2302 Assembly methodAssembly, bool isPrivate, bool isInternal, bool isStatic) 2303 { 2304 string message; 2305 2306 if (isStatic != (targetExpression is CodeTypeReferenceExpression)) 2307 { 2308 // If it's static, then the target object must be a type ref, and vice versa. 2309 2310 int errorNumber; 2311 2312 if (isStatic) 2313 { 2314 // We have "object.StaticMember" 2315 message = string.Format(CultureInfo.CurrentCulture, Messages.StaticMember, memberName); 2316 errorNumber = ErrorNumbers.Error_StaticMember; 2317 } 2318 else 2319 { 2320 // We have "TypeName.NonStaticMember" 2321 message = string.Format(CultureInfo.CurrentCulture, Messages.NonStaticMember, memberName); 2322 errorNumber = ErrorNumbers.Error_NonStaticMember; 2323 } 2324 2325 ValidationError error = new ValidationError(message, errorNumber); 2326 error.UserData[RuleUserDataKeys.ErrorObject] = parentExpr; 2327 Errors.Add(error); 2328 2329 return false; 2330 } 2331 2332 if (isPrivate && targetType != ThisType) 2333 { 2334 // Can't access private members except on the subject type. 2335 message = string.Format(CultureInfo.CurrentCulture, Messages.CannotAccessPrivateMember, memberName, RuleDecompiler.DecompileType(targetType)); 2336 ValidationError error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember); 2337 error.UserData[RuleUserDataKeys.ErrorObject] = parentExpr; 2338 Errors.Add(error); 2339 2340 return false; 2341 } 2342 2343 if (isInternal && ThisType.Assembly != methodAssembly) 2344 { 2345 // Can't access internal members except on the subject assembly. 2346 message = string.Format(CultureInfo.CurrentCulture, Messages.CannotAccessInternalMember, memberName, RuleDecompiler.DecompileType(targetType)); 2347 ValidationError error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember); 2348 error.UserData[RuleUserDataKeys.ErrorObject] = parentExpr; 2349 Errors.Add(error); 2350 2351 return false; 2352 } 2353 2354 return true; 2355 } 2356 2357 #region Field and property resolution 2358 ResolveFieldOrProperty(Type targetType, string name)2359 internal MemberInfo ResolveFieldOrProperty(Type targetType, string name) 2360 { 2361 BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy; 2362 if (AllowInternalMembers(targetType)) 2363 bindingFlags |= BindingFlags.NonPublic; 2364 2365 // Look up a field or property of the given name. 2366 MemberInfo[] results = targetType.GetMember(name, MemberTypes.Field | MemberTypes.Property, bindingFlags); 2367 2368 if (results != null) 2369 { 2370 int numResults = results.Length; 2371 if (numResults == 1) 2372 { 2373 // If we found exactly one, we're good. 2374 return results[0]; 2375 } 2376 else if (numResults > 1) 2377 { 2378 // We may have found more than one property if it's overloaded. If we find one without 2379 // any parameters, return that one. 2380 for (int i = 0; i < numResults; ++i) 2381 { 2382 MemberInfo member = results[i]; 2383 System.Diagnostics.Debug.Assert(member.MemberType == MemberTypes.Property, "only properties can be overloaded"); 2384 2385 PropertyInfo pi = (PropertyInfo)member; 2386 ParameterInfo[] parms = pi.GetIndexParameters(); 2387 if (parms == null || parms.Length == 0) 2388 { 2389 if (pi != null) 2390 { 2391 IsAuthorized(pi.PropertyType); 2392 } 2393 return pi; 2394 } 2395 } 2396 } 2397 } 2398 2399 // If we didn't find it, and if the target type is an interface, try resolving a property 2400 // that may exist in its inheritance chain. (Fields cannot appear on interfaces.) 2401 if (targetType.IsInterface) 2402 return ResolveProperty(targetType, name, bindingFlags); 2403 2404 // Otherwise, it's no good. 2405 return null; 2406 } 2407 ResolveProperty(Type targetType, string propertyName, BindingFlags bindingFlags)2408 internal PropertyInfo ResolveProperty(Type targetType, string propertyName, BindingFlags bindingFlags) 2409 { 2410 2411 PropertyInfo pi = GetProperty(targetType, propertyName, bindingFlags); 2412 if (pi == null && targetType.IsInterface) 2413 { 2414 Type[] parentInterfacesArray = targetType.GetInterfaces(); 2415 List<Type> parentInterfaces = new List<Type>(); 2416 parentInterfaces.AddRange(parentInterfacesArray); 2417 2418 int index = 0; 2419 while (index < parentInterfaces.Count) 2420 { 2421 pi = GetProperty(parentInterfaces[index], propertyName, bindingFlags); 2422 if (pi != null) 2423 break; 2424 2425 Type[] pInterfaces = parentInterfaces[index].GetInterfaces(); 2426 if (pInterfaces.Length > 0) 2427 parentInterfaces.AddRange(pInterfaces); 2428 ++index; 2429 } 2430 } 2431 2432 if (pi != null) 2433 { 2434 IsAuthorized(pi.PropertyType); 2435 } 2436 return pi; 2437 } 2438 GetProperty(Type targetType, string propertyName, BindingFlags bindingFlags)2439 private static PropertyInfo GetProperty(Type targetType, string propertyName, BindingFlags bindingFlags) 2440 { 2441 // Properties may be overloaded (in VB), so we have to ---- out those that we can support, 2442 // i.e., those that have no parameters. 2443 2444 MemberInfo[] members = targetType.GetMember(propertyName, MemberTypes.Property, bindingFlags); 2445 for (int m = 0; m < members.Length; ++m) 2446 { 2447 PropertyInfo pi = (PropertyInfo)members[m]; 2448 2449 ParameterInfo[] parms = pi.GetIndexParameters(); 2450 if (parms == null || parms.Length == 0) 2451 return pi; 2452 } 2453 2454 return null; 2455 } 2456 2457 #endregion 2458 2459 #region Method resolution 2460 2461 private class Argument 2462 { 2463 internal CodeExpression expression; 2464 internal FieldDirection direction; 2465 internal Type type; 2466 Argument(CodeExpression expr, RuleValidation validation)2467 internal Argument(CodeExpression expr, RuleValidation validation) 2468 { 2469 this.expression = expr; 2470 2471 this.direction = FieldDirection.In; 2472 CodeDirectionExpression directionExpr = expr as CodeDirectionExpression; 2473 if (directionExpr != null) 2474 this.direction = directionExpr.Direction; 2475 2476 this.type = validation.ExpressionInfo(expr).ExpressionType; 2477 } 2478 Argument(Type type)2479 internal Argument(Type type) 2480 { 2481 this.direction = FieldDirection.In; 2482 this.type = type; 2483 } 2484 } 2485 2486 private class CandidateParameter 2487 { 2488 private Type type; 2489 private FieldDirection direction; 2490 CandidateParameter(Type type)2491 internal CandidateParameter(Type type) 2492 { 2493 this.type = type; 2494 this.direction = FieldDirection.In; 2495 } 2496 CandidateParameter(ParameterInfo paramInfo)2497 internal CandidateParameter(ParameterInfo paramInfo) 2498 { 2499 this.direction = FieldDirection.In; 2500 if (paramInfo.IsOut) 2501 this.direction = FieldDirection.Out; 2502 else if (paramInfo.ParameterType.IsByRef) 2503 this.direction = FieldDirection.Ref; 2504 2505 this.type = paramInfo.ParameterType; 2506 } 2507 Match(Argument argument, string methodName, int argPosition, out ValidationError error)2508 internal bool Match(Argument argument, string methodName, int argPosition, out ValidationError error) 2509 { 2510 string message; 2511 2512 // If we don't agree on the argument direction, this method is not a candidate. 2513 if (this.direction != argument.direction) 2514 { 2515 string dirString = ""; 2516 switch (this.direction) 2517 { 2518 case FieldDirection.In: 2519 dirString = "in"; // No localization required, this is a keyword. 2520 break; 2521 case FieldDirection.Out: 2522 dirString = "out"; // No localization required, this is a keyword. 2523 break; 2524 case FieldDirection.Ref: 2525 dirString = "ref"; // No localization required, this is a keyword. 2526 break; 2527 } 2528 2529 message = string.Format(CultureInfo.CurrentCulture, Messages.MethodDirectionMismatch, argPosition, methodName, dirString); 2530 error = new ValidationError(message, ErrorNumbers.Error_MethodDirectionMismatch); 2531 2532 return false; 2533 } 2534 2535 if (this.type.IsByRef && this.type != argument.type) 2536 { 2537 // If the parameter is "ref" or "out", then the types must match exactly. 2538 // If not, this method can't be a candidate. 2539 2540 message = string.Format(CultureInfo.CurrentCulture, Messages.MethodArgumentTypeMismatch, argPosition, methodName, RuleDecompiler.DecompileType(argument.type), RuleDecompiler.DecompileType(this.type)); 2541 error = new ValidationError(message, ErrorNumbers.Error_MethodArgumentTypeMismatch); 2542 2543 return false; 2544 } 2545 2546 // If the argument type is not assignable to the corresponding parameter type, 2547 // this method can't be a candidate. 2548 if (!RuleValidation.TypesAreAssignable(argument.type, this.type, argument.expression, out error)) 2549 { 2550 if (error == null) 2551 { 2552 message = string.Format(CultureInfo.CurrentCulture, Messages.MethodArgumentTypeMismatch, argPosition, methodName, RuleDecompiler.DecompileType(argument.type), RuleDecompiler.DecompileType(this.type)); 2553 error = new ValidationError(message, ErrorNumbers.Error_MethodArgumentTypeMismatch); 2554 } 2555 return false; 2556 } 2557 2558 // If we passed the above checks, this argument is a candidate match for the parameter. 2559 return true; 2560 } 2561 Equals(object obj)2562 public override bool Equals(object obj) 2563 { 2564 CandidateParameter otherParam = obj as CandidateParameter; 2565 if (otherParam == null) 2566 return false; 2567 2568 return this.direction == otherParam.direction && this.type == otherParam.type; 2569 } 2570 GetHashCode()2571 public override int GetHashCode() 2572 { 2573 return this.direction.GetHashCode() ^ this.type.GetHashCode(); 2574 } 2575 CompareConversion(CandidateParameter otherParam, Argument argument)2576 internal int CompareConversion(CandidateParameter otherParam, Argument argument) 2577 { 2578 // Return 0 if they are equal; 1 if this is better; -1 if this is worse. 2579 // This follows the C# specification 7.4.2.3 2580 int better = 1; 2581 int worse = -1; 2582 int equal = 0; 2583 2584 // If the two candidate parameters have the same type, neither one is better. 2585 if (this.type == otherParam.type) 2586 return equal; 2587 2588 // If the argument type is the same as one of the parameter types, that parameter is better. 2589 if (argument.type == this.type) 2590 return better; 2591 if (argument.type == otherParam.type) 2592 return worse; 2593 2594 // If this parameter can be converted to the other parameter, and not vice versa, then 2595 // this is a better conversion. (And in the reverse situation, it's a worse conversion.) 2596 ValidationError dummy; 2597 bool thisConvertsToOther = RuleValidation.TypesAreAssignable(this.type, otherParam.type, null, out dummy); 2598 bool otherConvertsToThis = RuleValidation.TypesAreAssignable(otherParam.type, this.type, null, out dummy); 2599 if (thisConvertsToOther && !otherConvertsToThis) 2600 return better; 2601 if (otherConvertsToThis && !thisConvertsToOther) 2602 return worse; 2603 2604 // See if one is a better sign-preserving conversion than the other. 2605 if (BetterSignedConversion(this.type, otherParam.type)) 2606 return better; 2607 if (BetterSignedConversion(otherParam.type, this.type)) 2608 return worse; 2609 2610 // Otherwise, neither conversion is better. 2611 return equal; 2612 } 2613 2614 [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] BetterSignedConversion(Type t1, Type t2)2615 private static bool BetterSignedConversion(Type t1, Type t2) 2616 { 2617 TypeCode tc1 = Type.GetTypeCode(t1); 2618 TypeCode tc2 = Type.GetTypeCode(t2); 2619 2620 switch (tc1) 2621 { 2622 case TypeCode.SByte: 2623 switch (tc2) 2624 { 2625 case TypeCode.Byte: 2626 case TypeCode.UInt16: 2627 case TypeCode.UInt32: 2628 case TypeCode.UInt64: 2629 // A conversion to sbyte is better than a conversion to an unsigned type. 2630 return true; 2631 } 2632 break; 2633 2634 case TypeCode.Int16: 2635 switch (tc2) 2636 { 2637 case TypeCode.UInt16: 2638 case TypeCode.UInt32: 2639 case TypeCode.UInt64: 2640 // A conversion to short is better than a conversion to an unsigned type. 2641 return true; 2642 } 2643 break; 2644 2645 case TypeCode.Int32: 2646 if (tc2 == TypeCode.UInt32 || tc2 == TypeCode.UInt64) 2647 { 2648 // A conversion to int is better than a conversion to an unsigned type. 2649 return true; 2650 } 2651 break; 2652 2653 case TypeCode.Int64: 2654 if (tc2 == TypeCode.UInt64) 2655 { 2656 // A conversion to long is better than a conversion to an unsigned type. 2657 return true; 2658 } 2659 break; 2660 2661 case TypeCode.Object: 2662 // it is possible that the types are nullable 2663 if (ConditionHelper.IsNullableValueType(t1)) 2664 { 2665 t1 = t1.GetGenericArguments()[0]; 2666 // t2 may already be a value type 2667 if (ConditionHelper.IsNullableValueType(t2)) 2668 t2 = t2.GetGenericArguments()[0]; 2669 return BetterSignedConversion(t1, t2); 2670 } 2671 return false; 2672 } 2673 2674 return false; 2675 } 2676 } 2677 2678 private class CandidateMember 2679 { 2680 internal enum Form 2681 { 2682 Normal, // no "params" expansion 2683 Expanded // matched only after "params" expansion 2684 } 2685 2686 internal MemberInfo Member; 2687 private ParameterInfo[] memberParameters; 2688 private List<CandidateParameter> signature; 2689 private Form form; 2690 private static ParameterInfo[] noParameters = new ParameterInfo[0]; 2691 private static List<CandidateParameter> noSignature = new List<CandidateParameter>(); 2692 2693 // Constructor for candidate methods with parameters. CandidateMember(MemberInfo member, ParameterInfo[] parameters, List<CandidateParameter> signature, Form form)2694 internal CandidateMember(MemberInfo member, ParameterInfo[] parameters, List<CandidateParameter> signature, Form form) 2695 { 2696 this.Member = member; 2697 this.memberParameters = parameters; 2698 this.signature = signature; 2699 this.form = form; 2700 } 2701 2702 // Constructor for a candidate method that has no parameters. CandidateMember(MemberInfo member)2703 internal CandidateMember(MemberInfo member) 2704 : this(member, noParameters, noSignature, Form.Normal) 2705 { 2706 } 2707 2708 internal bool IsExpanded 2709 { 2710 get { return form == Form.Expanded; } 2711 } 2712 2713 [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] CompareMember(Type targetType, CandidateMember other, List<Argument> arguments, RuleValidation validator)2714 internal int CompareMember(Type targetType, CandidateMember other, List<Argument> arguments, RuleValidation validator) 2715 { 2716 int better = 1; 2717 int worse = -1; 2718 int equal = 0; 2719 2720 // Methods in a base class are not candidates if any method in a derived class 2721 // is applicable. 2722 Type thisDeclaringType = this.Member.DeclaringType; 2723 Type otherDeclaringType = other.Member.DeclaringType; 2724 if (thisDeclaringType != otherDeclaringType) 2725 { 2726 if (TypeProvider.IsAssignable(otherDeclaringType, thisDeclaringType)) 2727 { 2728 // This declaring type can be converted to the other declaring type, 2729 // which means this one is more derived. 2730 return better; 2731 } 2732 else if (TypeProvider.IsAssignable(thisDeclaringType, otherDeclaringType)) 2733 { 2734 // The other declaring type can be converted to this declaring type, 2735 // which means the other one is more derived. 2736 return worse; 2737 } 2738 } 2739 2740 System.Diagnostics.Debug.Assert(arguments.Count == this.signature.Count); 2741 System.Diagnostics.Debug.Assert(arguments.Count == other.signature.Count); 2742 2743 bool hasAtLeastOneBetterConversion = false; 2744 bool hasAtLeastOneWorseConversion = false; 2745 bool signaturesAreIdentical = true; 2746 2747 // pick non-extension methods over extension methods 2748 // if both are extension methods, then pick the one in the namespace closest to "this" 2749 ExtensionMethodInfo thisExtension = this.Member as ExtensionMethodInfo; 2750 ExtensionMethodInfo otherExtension = other.Member as ExtensionMethodInfo; 2751 if ((thisExtension == null) && (otherExtension != null)) 2752 return better; 2753 else if ((thisExtension != null) && (otherExtension == null)) 2754 return worse; 2755 else if ((thisExtension != null) && (otherExtension != null)) 2756 { 2757 // we have 2 extension methods, which one is better 2758 string[] thisNameSpace = thisExtension.DeclaringType.FullName.Split('.'); 2759 string[] otherNameSpace = otherExtension.DeclaringType.FullName.Split('.'); 2760 string[] bestNameSpace = validator.thisType.FullName.Split('.'); 2761 int thisMatch = MatchNameSpace(thisNameSpace, bestNameSpace); 2762 int otherMatch = MatchNameSpace(otherNameSpace, bestNameSpace); 2763 if (thisMatch > otherMatch) 2764 return better; 2765 else if (thisMatch < otherMatch) 2766 return worse; 2767 2768 // compare arguments, including the "this" argument 2769 CandidateParameter thisDeclaringParam = new CandidateParameter(thisExtension.AssumedDeclaringType); 2770 CandidateParameter otherDeclaringParam = new CandidateParameter(otherExtension.AssumedDeclaringType); 2771 if (!thisDeclaringParam.Equals(otherDeclaringParam)) 2772 { 2773 signaturesAreIdentical = false; 2774 int conversionResult = thisDeclaringParam.CompareConversion(otherDeclaringParam, new Argument(targetType)); 2775 if (conversionResult < 0) 2776 { 2777 // A conversion was found that was worse, so this candidate is not better. 2778 hasAtLeastOneWorseConversion = true; 2779 } 2780 else if (conversionResult > 0) 2781 { 2782 // This candidate had at least one conversion that was better. (But 2783 // we have to keep looking in case there's one that's worse.) 2784 hasAtLeastOneBetterConversion = true; 2785 } 2786 } 2787 2788 // this check compares parameter lists correctly (see below) 2789 for (int p = 0; p < arguments.Count; ++p) 2790 { 2791 CandidateParameter thisParam = this.signature[p]; 2792 CandidateParameter otherParam = other.signature[p]; 2793 2794 if (!thisParam.Equals(otherParam)) 2795 signaturesAreIdentical = false; 2796 2797 int conversionResult = thisParam.CompareConversion(otherParam, arguments[p]); 2798 if (conversionResult < 0) 2799 { 2800 // A conversion was found that was worse, so this candidate is not better. 2801 hasAtLeastOneWorseConversion = true; 2802 } 2803 else if (conversionResult > 0) 2804 { 2805 // This candidate had at least one conversion that was better. (But 2806 // we have to keep looking in case there's one that's worse.) 2807 hasAtLeastOneBetterConversion = true; 2808 } 2809 } 2810 if (hasAtLeastOneBetterConversion && !hasAtLeastOneWorseConversion) 2811 { 2812 // At least one conversion was better than the "other" candidate 2813 // and no other arguments were worse, so this one is better. 2814 return better; 2815 } 2816 else if (!hasAtLeastOneBetterConversion && hasAtLeastOneWorseConversion) 2817 { 2818 // At least one conversion was worse than the "other" candidate 2819 // and no other arguments were better, so this one is worse. 2820 return worse; 2821 } 2822 } 2823 else 2824 { 2825 // NOTE: this is the original v1 code 2826 // It doesn't check for worse parameters correctly. 2827 // However, for backwards compatability, we can't change it 2828 for (int p = 0; p < arguments.Count; ++p) 2829 { 2830 CandidateParameter thisParam = this.signature[p]; 2831 CandidateParameter otherParam = other.signature[p]; 2832 2833 if (!thisParam.Equals(otherParam)) 2834 signaturesAreIdentical = false; 2835 2836 int conversionResult = thisParam.CompareConversion(otherParam, arguments[p]); 2837 if (conversionResult < 0) 2838 { 2839 // A conversion was found that was worse, so this candidate is not better. 2840 return worse; 2841 } 2842 else if (conversionResult > 0) 2843 { 2844 // This candidate had at least one conversion that was better. (But 2845 // we have to keep looking in case there's one that's worse.) 2846 hasAtLeastOneBetterConversion = true; 2847 } 2848 } 2849 2850 if (hasAtLeastOneBetterConversion) 2851 { 2852 // At least one conversion was better than the "other" candidate, so this one 2853 // is better. 2854 return better; 2855 } 2856 } 2857 2858 if (signaturesAreIdentical) 2859 { 2860 // The signatures were "tied". Try some disambiguating rules for expanded signatures 2861 // vs normal signatures. 2862 if (this.form == Form.Normal && other.form == Form.Expanded) 2863 { 2864 // This candidate matched in its normal form, but the other one matched only after 2865 // expansion of a params array. This one is better. 2866 return better; 2867 } 2868 else if (this.form == Form.Expanded && other.form == Form.Normal) 2869 { 2870 // This candidate matched in its expanded form, but the other one matched in its 2871 // normal form. The other one was better. 2872 return worse; 2873 } 2874 else if (this.form == Form.Expanded && other.form == Form.Expanded) 2875 { 2876 // Both candidates matched in their expanded forms. 2877 2878 int thisParameterCount = this.memberParameters.Length; 2879 int otherParameterCount = other.memberParameters.Length; 2880 2881 if (thisParameterCount > otherParameterCount) 2882 { 2883 // This candidate had more declared parameters, so it is better. 2884 return better; 2885 } 2886 else if (otherParameterCount > thisParameterCount) 2887 { 2888 // The other candidate had more declared parameters, so it was better. 2889 return worse; 2890 } 2891 } 2892 } 2893 2894 // Nothing worked, the two candidates are equally applicable. 2895 return equal; 2896 } 2897 MatchNameSpace(string[] test, string[] reference)2898 private static int MatchNameSpace(string[] test, string[] reference) 2899 { 2900 // returns the number of strings in test that are the same as reference 2901 int i; 2902 int len = Math.Min(test.Length, reference.Length); 2903 for (i = 0; i < len; ++i) 2904 { 2905 if (test[i] != reference[i]) 2906 break; 2907 } 2908 return i; 2909 } 2910 } 2911 2912 // Get the candidate target types, ordered from most-derived to least-derived. GetCandidateTargetTypes(Type targetType)2913 private static List<Type> GetCandidateTargetTypes(Type targetType) 2914 { 2915 List<Type> candidateTypes; 2916 2917 if (targetType.IsInterface) 2918 { 2919 candidateTypes = new List<Type>(); 2920 candidateTypes.Add(targetType); 2921 2922 // Add all base interfaces in its hierarchy to the candidate list. 2923 for (int i = 0; i < candidateTypes.Count; ++i) 2924 { 2925 Type currentCandidate = candidateTypes[i]; 2926 candidateTypes.AddRange(currentCandidate.GetInterfaces()); 2927 } 2928 2929 // Finally, add "System.Object", since all types intrinsically derive from this. 2930 candidateTypes.Add(typeof(object)); 2931 } 2932 else 2933 { 2934 // It was a class; just add the one class. 2935 candidateTypes = new List<Type>(1); 2936 candidateTypes.Add(targetType); 2937 } 2938 2939 return candidateTypes; 2940 } 2941 BuildArgCountMismatchError(string name, int numArguments)2942 private delegate ValidationError BuildArgCountMismatchError(string name, int numArguments); 2943 EvaluateCandidate(List<CandidateMember> candidates, MemberInfo candidateMember, ParameterInfo[] parameters, List<Argument> arguments, out ValidationError error, BuildArgCountMismatchError buildArgCountMismatchError)2944 private static void EvaluateCandidate(List<CandidateMember> candidates, MemberInfo candidateMember, ParameterInfo[] parameters, List<Argument> arguments, out ValidationError error, BuildArgCountMismatchError buildArgCountMismatchError) 2945 { 2946 error = null; 2947 2948 int numArguments = arguments.Count; 2949 string candidateName = candidateMember.Name; 2950 2951 if (parameters == null || parameters.Length == 0) 2952 { 2953 // If there were no arguments supplied, and this method has no parameters, 2954 // then it's a candidate. (It should be the only one.) 2955 if (numArguments == 0) 2956 { 2957 candidates.Add(new CandidateMember(candidateMember)); 2958 } 2959 else 2960 { 2961 error = buildArgCountMismatchError(candidateName, numArguments); 2962 } 2963 } 2964 else 2965 { 2966 List<CandidateParameter> signature = new List<CandidateParameter>(); 2967 2968 int parameterCount = parameters.Length; 2969 2970 int fixedParameterCount = parameterCount; 2971 2972 // Check to see if the last parameter is (1) an array and (2) has a ParamArrayAttribute 2973 // (i.e., it is a "params" array). 2974 ParameterInfo lastParam = parameters[parameterCount - 1]; 2975 if (lastParam.ParameterType.IsArray) 2976 { 2977 object[] attrs = lastParam.GetCustomAttributes(typeof(ParamArrayAttribute), false); 2978 if (attrs != null && attrs.Length > 0) 2979 fixedParameterCount -= 1; 2980 } 2981 2982 if (numArguments < fixedParameterCount) 2983 { 2984 // Not enough arguments were passed for this to be a candidate. 2985 error = buildArgCountMismatchError(candidateName, numArguments); 2986 2987 return; 2988 } 2989 else if (fixedParameterCount == parameterCount && numArguments != parameterCount) 2990 { 2991 // Too many arguments were passed for this to be a candidate. 2992 error = buildArgCountMismatchError(candidateName, numArguments); 2993 2994 return; 2995 } 2996 2997 // For the fixed part of the method signature, make sure each argument can 2998 // be implicitly converted to the corresponding parameter. 2999 int p = 0; 3000 for (; p < fixedParameterCount; ++p) 3001 { 3002 CandidateParameter candidateParam = new CandidateParameter(parameters[p]); 3003 if (!candidateParam.Match(arguments[p], candidateName, p + 1, out error)) 3004 break; // argument #p didn't match 3005 3006 // If we get here, then so far so good. 3007 signature.Add(candidateParam); 3008 } 3009 3010 if (p != fixedParameterCount) 3011 { 3012 // We didn't match all of the fixed part. This method is not a candidate. 3013 return; 3014 } 3015 3016 if (fixedParameterCount < parameterCount) 3017 { 3018 // The last parameter was a "params" array. As long as zero or more arguments 3019 // are assignable, it's a valid candidate in the expanded form. 3020 3021 CandidateMember candidateMethod = null; 3022 3023 if (numArguments == fixedParameterCount) 3024 { 3025 // Zero arguments were passed as the params array. The method is a candidate 3026 // in its expanded form. 3027 candidateMethod = new CandidateMember(candidateMember, parameters, signature, CandidateMember.Form.Expanded); 3028 } 3029 else if (numArguments == parameterCount) 3030 { 3031 // Special case: one argument was passed as the params array. 3032 CandidateParameter candidateParam = new CandidateParameter(lastParam); 3033 if (candidateParam.Match(arguments[p], candidateName, p + 1, out error)) 3034 { 3035 // It was the same array type as the params array, so the candidate 3036 // matched in its normal form. 3037 signature.Add(candidateParam); 3038 candidateMethod = new CandidateMember(candidateMember, parameters, signature, CandidateMember.Form.Normal); 3039 } 3040 } 3041 3042 if (candidateMethod == null) 3043 { 3044 // One or more arguments were passed as the params array. As long 3045 // as they match the element type, this method is a candidate. 3046 CandidateParameter candidateParam = new CandidateParameter(lastParam.ParameterType.GetElementType()); 3047 3048 for (; p < numArguments; ++p) 3049 { 3050 if (!candidateParam.Match(arguments[p], candidateName, p + 1, out error)) 3051 { 3052 // Not all of the trailing arguments matched the params array's element type; 3053 // this cannot be a candidate. 3054 return; 3055 } 3056 3057 // If we get here, then so far so good. 3058 signature.Add(candidateParam); 3059 } 3060 3061 // All the trailing arguments matched, so this is a candidate in the expanded form. 3062 candidateMethod = new CandidateMember(candidateMember, parameters, signature, CandidateMember.Form.Expanded); 3063 } 3064 3065 candidates.Add(candidateMethod); 3066 } 3067 else 3068 { 3069 // The last parameter wasn't "params". This candidate matched in its normal form. 3070 candidates.Add(new CandidateMember(candidateMember, parameters, signature, CandidateMember.Form.Normal)); 3071 } 3072 } 3073 } 3074 FindBestCandidate(Type targetType, List<CandidateMember> candidates, List<Argument> arguments)3075 private CandidateMember FindBestCandidate(Type targetType, List<CandidateMember> candidates, List<Argument> arguments) 3076 { 3077 int numCandidates = candidates.Count; 3078 Debug.Assert(numCandidates > 0, "expected at least one candidate"); 3079 3080 // Start by assuming the first candidate is the best one. 3081 List<CandidateMember> bestCandidates = new List<CandidateMember>(1); 3082 bestCandidates.Add(candidates[0]); 3083 3084 // Go through the rest of the candidates and try to find a better one. (If 3085 // there are no more candidates, then there was only one, and that's the right 3086 // one.) 3087 for (int i = 1; i < numCandidates; ++i) 3088 { 3089 CandidateMember newCandidate = candidates[i]; 3090 3091 // Compare this new candidate one if the current "best" ones. (If there 3092 // is currently more than one best candidate, then so far its ambiguous, which 3093 // means all the best ones are equally good. Thus if this new candidate 3094 // is better than one, it's better than all. 3095 CandidateMember bestCandidate = bestCandidates[0]; 3096 3097 int comparison = newCandidate.CompareMember(targetType, bestCandidate, arguments, this); 3098 if (comparison > 0) 3099 { 3100 // The new one was better than at least one of the best ones. It 3101 // becomes the new best one. 3102 bestCandidates.Clear(); 3103 bestCandidates.Add(newCandidate); 3104 } 3105 else if (comparison == 0) 3106 { 3107 // The new one was no better, so add it to the list of current best. 3108 // (Unless we find a better one, it's ambiguous so far.) 3109 bestCandidates.Add(newCandidate); 3110 } 3111 } 3112 3113 if (bestCandidates.Count == 1) 3114 { 3115 // Good, there was exactly one best match. 3116 return bestCandidates[0]; 3117 } 3118 3119 // Otherwise, it must have been ambiguous. 3120 return null; 3121 } 3122 FindBestCandidate(Type targetType, List<MethodInfo> methods, params Type[] types)3123 internal MethodInfo FindBestCandidate(Type targetType, List<MethodInfo> methods, params Type[] types) 3124 { 3125 List<Argument> arguments = new List<Argument>(); 3126 foreach (Type t in types) 3127 arguments.Add(new Argument(t)); 3128 3129 List<CandidateMember> candidates = new List<CandidateMember>(methods.Count); 3130 foreach (MethodInfo method in methods) 3131 { 3132 ValidationError tempError = null; 3133 EvaluateCandidate(candidates, method, method.GetParameters(), arguments, out tempError, 3134 delegate(string name, int numArguments) 3135 { 3136 string message = string.Format(CultureInfo.CurrentCulture, Messages.MethodArgCountMismatch, name, numArguments); 3137 return new ValidationError(message, ErrorNumbers.Error_MethodArgCountMismatch); 3138 }); 3139 } 3140 if (candidates.Count == 0) 3141 { 3142 // nothing looks useful 3143 return null; 3144 } 3145 CandidateMember result = FindBestCandidate(targetType, candidates, arguments); 3146 return (result != null) ? (MethodInfo)result.Member : null; 3147 } 3148 ResolveConstructor(Type targetType, BindingFlags constructorBindingFlags, List<CodeExpression> argumentExprs, out ValidationError error)3149 internal RuleConstructorExpressionInfo ResolveConstructor(Type targetType, BindingFlags constructorBindingFlags, List<CodeExpression> argumentExprs, out ValidationError error) 3150 { 3151 string message; 3152 3153 List<Argument> arguments = new List<Argument>(argumentExprs.Count); 3154 foreach (CodeExpression argumentExpr in argumentExprs) 3155 arguments.Add(new Argument(argumentExpr, this)); 3156 3157 // Get the candidate types and all candidate methods contained in them. 3158 List<Type> candidateTypes = GetCandidateTargetTypes(targetType); 3159 // Get all methods by this name... 3160 List<ConstructorInfo> constructors = GetConstructors(candidateTypes, constructorBindingFlags); 3161 if (constructors.Count == 0) 3162 { 3163 message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownConstructor, RuleDecompiler.DecompileType(targetType)); 3164 error = new ValidationError(message, ErrorNumbers.Error_MethodNotExists); 3165 return null; 3166 } 3167 3168 // Cull the list of methods to those which match the supplied arguments. 3169 List<CandidateMember> candidateConstructors = GetCandidateConstructors(constructors, arguments, out error); 3170 3171 // If the list is null, then no candidates matched. 3172 if (candidateConstructors == null) 3173 return null; 3174 3175 // We found candidate methods in this type. 3176 CandidateMember bestCandidate = FindBestCandidate(targetType, candidateConstructors, arguments); 3177 3178 if (bestCandidate == null) 3179 { 3180 // It was ambiguous. 3181 message = string.Format(CultureInfo.CurrentCulture, Messages.AmbiguousConstructor, RuleDecompiler.DecompileType(targetType)); 3182 error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember); 3183 return null; 3184 } 3185 3186 // We found the best match. 3187 return new RuleConstructorExpressionInfo((ConstructorInfo)bestCandidate.Member, bestCandidate.IsExpanded); 3188 } 3189 ResolveMethod(Type targetType, string methodName, BindingFlags methodBindingFlags, List<CodeExpression> argumentExprs, out ValidationError error)3190 internal RuleMethodInvokeExpressionInfo ResolveMethod(Type targetType, string methodName, BindingFlags methodBindingFlags, List<CodeExpression> argumentExprs, out ValidationError error) 3191 { 3192 string message; 3193 3194 List<Argument> arguments = new List<Argument>(argumentExprs.Count); 3195 foreach (CodeExpression argumentExpr in argumentExprs) 3196 arguments.Add(new Argument(argumentExpr, this)); 3197 3198 // Get the candidate types and all candidate methods contained in them. 3199 List<Type> candidateTypes = GetCandidateTargetTypes(targetType); 3200 // Get all methods by this name... 3201 List<MethodInfo> methods = GetNamedMethods(candidateTypes, methodName, methodBindingFlags); 3202 if (methods.Count == 0) 3203 { 3204 message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownMethod, methodName, RuleDecompiler.DecompileType(targetType)); 3205 error = new ValidationError(message, ErrorNumbers.Error_MethodNotExists); 3206 return null; 3207 } 3208 3209 // Cull the list of methods to those which match the supplied arguments. 3210 List<CandidateMember> candidateMethods = GetCandidateMethods(methodName, methods, arguments, out error); 3211 3212 // If the list is null, then no candidates matched. 3213 if (candidateMethods == null) 3214 return null; 3215 3216 // We found candidate methods in this type. 3217 CandidateMember bestCandidate = FindBestCandidate(targetType, candidateMethods, arguments); 3218 3219 if (bestCandidate == null) 3220 { 3221 // It was ambiguous. 3222 message = string.Format(CultureInfo.CurrentCulture, Messages.AmbiguousMatch, methodName); 3223 error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember); 3224 3225 return null; 3226 } 3227 3228 // We found the best match. 3229 MethodInfo theMethod = (MethodInfo)bestCandidate.Member; 3230 if (theMethod != null) 3231 { 3232 IsAuthorized(theMethod.ReturnType); 3233 } 3234 return new RuleMethodInvokeExpressionInfo(theMethod, bestCandidate.IsExpanded); 3235 } 3236 GetConstructors(List<Type> targetTypes, BindingFlags constructorBindingFlags)3237 internal static List<ConstructorInfo> GetConstructors(List<Type> targetTypes, BindingFlags constructorBindingFlags) 3238 { 3239 List<ConstructorInfo> methods = new List<ConstructorInfo>(); 3240 3241 for (int t = 0; t < targetTypes.Count; ++t) 3242 { 3243 Type targetType = targetTypes[t]; 3244 3245 // Go through all the constructors on the target type 3246 ConstructorInfo[] members = targetType.GetConstructors(constructorBindingFlags); 3247 for (int m = 0; m < members.Length; ++m) 3248 { 3249 ConstructorInfo constructor = members[m]; 3250 if (constructor.IsGenericMethod) // skip generic constructors 3251 continue; 3252 if (constructor.IsStatic) // skip static constructors 3253 continue; 3254 if (constructor.IsPrivate) // skip private constructors 3255 continue; 3256 if (constructor.IsFamily) // skip internal constructors 3257 continue; 3258 methods.Add(constructor); 3259 } 3260 } 3261 return methods; 3262 } 3263 GetNamedMethods(List<Type> targetTypes, string methodName, BindingFlags methodBindingFlags)3264 private List<MethodInfo> GetNamedMethods(List<Type> targetTypes, string methodName, BindingFlags methodBindingFlags) 3265 { 3266 List<MethodInfo> methods = new List<MethodInfo>(); 3267 List<ExtensionMethodInfo> currentExtensionMethods = ExtensionMethods; 3268 for (int t = 0; t < targetTypes.Count; ++t) 3269 { 3270 Type targetType = targetTypes[t]; 3271 3272 // Go through all the methods on the target type that have matching names. 3273 MemberInfo[] members = targetType.GetMember(methodName, MemberTypes.Method, methodBindingFlags); 3274 for (int m = 0; m < members.Length; ++m) 3275 { 3276 MethodInfo method = (MethodInfo)members[m]; 3277 if (!method.IsGenericMethod) // skip generic methods 3278 methods.Add(method); 3279 } 3280 3281 // add in any extension methods that match 3282 foreach (ExtensionMethodInfo extension in currentExtensionMethods) 3283 { 3284 // does it have the right name and is the type compatible 3285 ValidationError error; 3286 if ((extension.Name == methodName) && 3287 TypesAreAssignable(targetType, extension.AssumedDeclaringType, null, out error)) 3288 { 3289 // possible match 3290 methods.Add(extension); 3291 } 3292 } 3293 } 3294 3295 return methods; 3296 } 3297 3298 private List<ExtensionMethodInfo> extensionMethods; 3299 private List<Assembly> seenAssemblies; 3300 private const string ExtensionAttributeFullName = "System.Runtime.CompilerServices.ExtensionAttribute, " + AssemblyRef.SystemCore; 3301 private Type extensionAttribute; 3302 3303 private static Type defaultExtensionAttribute = GetDefaultExtensionAttribute(); 3304 GetDefaultExtensionAttribute()3305 private static Type GetDefaultExtensionAttribute() 3306 { 3307 return Type.GetType(ExtensionAttributeFullName, false); 3308 } 3309 3310 // The extensionAttributeType may still be null after calling this method 3311 // if, for example, we are in a 3.0 SP2 environment. SetExtensionAttribute()3312 private void SetExtensionAttribute() 3313 { 3314 // use the TypeProvider first 3315 extensionAttribute = typeProvider.GetType(ExtensionAttributeFullName, false); 3316 if (extensionAttribute == null) 3317 { 3318 extensionAttribute = defaultExtensionAttribute; 3319 } 3320 } 3321 3322 internal List<ExtensionMethodInfo> ExtensionMethods 3323 { 3324 get 3325 { 3326 if (extensionMethods == null) 3327 DetermineExtensionMethods(); 3328 3329 return extensionMethods; 3330 } 3331 } 3332 DetermineExtensionMethods()3333 private void DetermineExtensionMethods() 3334 { 3335 extensionMethods = new List<ExtensionMethodInfo>(); 3336 3337 SetExtensionAttribute(); 3338 if (extensionAttribute != null) 3339 { 3340 seenAssemblies = new List<Assembly>(); 3341 Assembly localAssembly = typeProvider.LocalAssembly; 3342 if (localAssembly != null) 3343 { 3344 DetermineExtensionMethods(localAssembly); 3345 foreach (Assembly a in typeProvider.ReferencedAssemblies) 3346 DetermineExtensionMethods(a); 3347 } 3348 else 3349 { 3350 // probably at design-time, nothing compiled yet 3351 // go through all types it knows about 3352 DetermineExtensionMethods(typeProvider.GetTypes()); 3353 } 3354 } 3355 } 3356 DetermineExtensionMethods(Assembly assembly)3357 internal void DetermineExtensionMethods(Assembly assembly) 3358 { 3359 // when this method is called outside of this class, we must have tried 3360 // getting ExtensionMethods. So we must have tried setting extensionAttributeType. 3361 3362 if (extensionAttribute != null) 3363 { 3364 if ((assembly != null) && (!seenAssemblies.Contains(assembly))) 3365 { 3366 seenAssemblies.Add(assembly); 3367 if (IsMarkedExtension(assembly)) 3368 { 3369 Type[] types; 3370 try 3371 { 3372 types = assembly.GetTypes(); 3373 } 3374 catch (ReflectionTypeLoadException e) 3375 { 3376 // problems loading all the types, take what we can get 3377 // some types will be null 3378 types = e.Types; 3379 } 3380 DetermineExtensionMethods(types); 3381 } 3382 } 3383 } 3384 } 3385 DetermineExtensionMethods(Type[] types)3386 private void DetermineExtensionMethods(Type[] types) 3387 { 3388 foreach (Type type in types) 3389 { 3390 // static classes are defined as "abstract sealed" 3391 // Note: VB doesn't support static classes, so the modules are only defined as "sealed" 3392 if ((type != null) && (type.IsPublic || type.IsNestedPublic) && (type.IsSealed) && (IsMarkedExtension(type))) 3393 { 3394 // looks like a class containing extension methods, let's find them 3395 MethodInfo[] staticMethods = type.GetMethods(BindingFlags.Static | BindingFlags.Public); 3396 foreach (MethodInfo mi in staticMethods) 3397 { 3398 // skip generic methods 3399 if ((mi.IsStatic) && !(mi.IsGenericMethod) && (IsMarkedExtension(mi))) 3400 { 3401 ParameterInfo[] parms = mi.GetParameters(); 3402 if (parms.Length > 0 && parms[0].ParameterType != null) 3403 { 3404 extensionMethods.Add(new ExtensionMethodInfo(mi, parms)); 3405 } 3406 } 3407 } 3408 } 3409 } 3410 } 3411 IsMarkedExtension(Assembly assembly)3412 private bool IsMarkedExtension(Assembly assembly) 3413 { 3414 if (extensionAttribute != null) 3415 { 3416 object[] objAttrs = assembly.GetCustomAttributes(extensionAttribute, false); 3417 if (objAttrs != null && objAttrs.Length > 0) 3418 return true; 3419 } 3420 3421 return false; 3422 } 3423 IsMarkedExtension(Type type)3424 private bool IsMarkedExtension(Type type) 3425 { 3426 if (extensionAttribute != null) 3427 { 3428 object[] objAttrs = type.GetCustomAttributes(extensionAttribute, false); 3429 if (objAttrs != null && objAttrs.Length > 0) 3430 return true; 3431 } 3432 3433 return false; 3434 } 3435 IsMarkedExtension(MethodInfo mi)3436 private bool IsMarkedExtension(MethodInfo mi) 3437 { 3438 if (extensionAttribute != null) 3439 { 3440 object[] objAttrs = mi.GetCustomAttributes(extensionAttribute, false); 3441 if (objAttrs != null && objAttrs.Length > 0) 3442 return true; 3443 } 3444 3445 return false; 3446 } 3447 GetCandidateMethods(string methodName, List<MethodInfo> methods, List<Argument> arguments, out ValidationError error)3448 static List<CandidateMember> GetCandidateMethods(string methodName, List<MethodInfo> methods, List<Argument> arguments, out ValidationError error) 3449 { 3450 List<CandidateMember> candidates = new List<CandidateMember>(); 3451 3452 error = null; 3453 3454 int errorCount = 0; 3455 foreach (MethodInfo method in methods) 3456 { 3457 ValidationError tempError = null; 3458 EvaluateCandidate(candidates, method, method.GetParameters(), arguments, out tempError, 3459 delegate(string name, int numArguments) 3460 { 3461 string message = string.Format(CultureInfo.CurrentCulture, Messages.MethodArgCountMismatch, name, numArguments); 3462 return new ValidationError(message, ErrorNumbers.Error_MethodArgCountMismatch); 3463 }); 3464 3465 error = tempError; 3466 if (tempError != null) 3467 ++errorCount; 3468 } 3469 3470 if (candidates.Count == 0) 3471 { 3472 // No candidates were found. 3473 3474 if (errorCount > 1) 3475 { 3476 // If multiple candidates generated errors, then use a more generic error that says 3477 // we couldn't find a matching overload. 3478 string message = string.Format(CultureInfo.CurrentCulture, Messages.MethodOverloadNotFound, methodName); 3479 error = new ValidationError(message, ErrorNumbers.Error_MethodOverloadNotFound); 3480 } 3481 3482 return null; 3483 } 3484 else 3485 { 3486 // If there are any candidates, then wipe out any errors left over from any mismatches. 3487 error = null; 3488 } 3489 3490 return candidates; 3491 } 3492 GetCandidateConstructors(List<ConstructorInfo> constructors, List<Argument> arguments, out ValidationError error)3493 static List<CandidateMember> GetCandidateConstructors(List<ConstructorInfo> constructors, List<Argument> arguments, out ValidationError error) 3494 { 3495 List<CandidateMember> candidates = new List<CandidateMember>(); 3496 3497 error = null; 3498 3499 int errorCount = 0; 3500 foreach (ConstructorInfo method in constructors) 3501 { 3502 ValidationError tempError = null; 3503 EvaluateCandidate(candidates, method, method.GetParameters(), arguments, out tempError, 3504 delegate(string name, int numArguments) 3505 { 3506 string message = string.Format(CultureInfo.CurrentCulture, Messages.MethodArgCountMismatch, name, numArguments); 3507 return new ValidationError(message, ErrorNumbers.Error_MethodArgCountMismatch); 3508 }); 3509 3510 error = tempError; 3511 if (tempError != null) 3512 ++errorCount; 3513 } 3514 3515 if (candidates.Count == 0) 3516 { 3517 // No candidates were found. 3518 3519 if (errorCount > 1) 3520 { 3521 // If multiple candidates generated errors, then use a more generic error that says 3522 // we couldn't find a matching overload. 3523 string message = string.Format(CultureInfo.CurrentCulture, Messages.ConstructorOverloadNotFound); 3524 error = new ValidationError(message, ErrorNumbers.Error_MethodOverloadNotFound); 3525 } 3526 3527 return null; 3528 } 3529 else 3530 { 3531 // If there are any candidates, then wipe out any errors left over from any mismatches. 3532 error = null; 3533 } 3534 3535 return candidates; 3536 } 3537 ResolveIndexerProperty(Type targetType, BindingFlags bindingFlags, List<CodeExpression> argumentExprs, out ValidationError error)3538 internal RulePropertyExpressionInfo ResolveIndexerProperty(Type targetType, BindingFlags bindingFlags, List<CodeExpression> argumentExprs, out ValidationError error) 3539 { 3540 string message; 3541 3542 int numArgs = argumentExprs.Count; 3543 3544 if (numArgs < 1) 3545 { 3546 // Must have at least one indexer! 3547 message = string.Format(CultureInfo.CurrentCulture, Messages.IndexerCountMismatch, numArgs); 3548 error = new ValidationError(message, ErrorNumbers.Error_IndexerCountMismatch); 3549 return null; 3550 } 3551 3552 List<Argument> arguments = new List<Argument>(numArgs); 3553 foreach (CodeExpression argumentExpr in argumentExprs) 3554 arguments.Add(new Argument(argumentExpr, this)); 3555 3556 // Get the candidate types and all the candidate indexer properties contained in them. 3557 List<Type> candidateTypes = GetCandidateTargetTypes(targetType); 3558 List<PropertyInfo> indexerProperties = GetIndexerProperties(candidateTypes, bindingFlags); 3559 if (indexerProperties.Count == 0) 3560 { 3561 message = string.Format(CultureInfo.CurrentCulture, Messages.IndexerNotFound, RuleDecompiler.DecompileType(targetType)); 3562 error = new ValidationError(message, ErrorNumbers.Error_IndexerNotFound); 3563 return null; 3564 } 3565 3566 List<CandidateMember> candidateIndexers = GetCandidateIndexers(indexerProperties, arguments, out error); 3567 3568 // If the list is null, then no candidates matched. 3569 if (candidateIndexers == null) 3570 return null; 3571 3572 // We found candidate methods in this type. 3573 CandidateMember bestCandidate = FindBestCandidate(targetType, candidateIndexers, arguments); 3574 3575 if (bestCandidate == null) 3576 { 3577 // It was ambiguous. 3578 message = string.Format(CultureInfo.CurrentCulture, Messages.AmbiguousIndexerMatch); 3579 error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember); 3580 3581 return null; 3582 } 3583 3584 // We found the best match. 3585 PropertyInfo pi = (PropertyInfo)bestCandidate.Member; 3586 if (pi != null) 3587 { 3588 IsAuthorized(pi.PropertyType); 3589 } 3590 return new RulePropertyExpressionInfo(pi, pi.PropertyType, bestCandidate.IsExpanded); 3591 } 3592 GetIndexerProperties(List<Type> candidateTypes, BindingFlags bindingFlags)3593 private static List<PropertyInfo> GetIndexerProperties(List<Type> candidateTypes, BindingFlags bindingFlags) 3594 { 3595 List<PropertyInfo> indexerProperties = new List<PropertyInfo>(); 3596 3597 foreach (Type targetType in candidateTypes) 3598 { 3599 object[] attrs = targetType.GetCustomAttributes(typeof(DefaultMemberAttribute), true); 3600 if (attrs == null || attrs.Length == 0) 3601 continue; 3602 3603 DefaultMemberAttribute[] defaultMemberAttrs = (DefaultMemberAttribute[])attrs; 3604 3605 PropertyInfo[] properties = targetType.GetProperties(bindingFlags); 3606 for (int p = 0; p < properties.Length; ++p) 3607 { 3608 PropertyInfo pi = properties[p]; 3609 3610 // Select only those properties whose name matches the default name. 3611 bool matchedName = false; 3612 for (int dm = 0; dm < defaultMemberAttrs.Length; ++dm) 3613 { 3614 if (defaultMemberAttrs[dm].MemberName == pi.Name) 3615 { 3616 matchedName = true; 3617 break; 3618 } 3619 } 3620 3621 if (matchedName) 3622 { 3623 // We matched the name... 3624 ParameterInfo[] indexerParameters = pi.GetIndexParameters(); 3625 if (indexerParameters.Length > 0) 3626 { 3627 // ... and have indexer parameters; therefore, this is 3628 // an interesting property. 3629 indexerProperties.Add(pi); 3630 } 3631 } 3632 } 3633 } 3634 3635 return indexerProperties; 3636 } 3637 GetCandidateIndexers(List<PropertyInfo> indexerProperties, List<Argument> arguments, out ValidationError error)3638 private static List<CandidateMember> GetCandidateIndexers(List<PropertyInfo> indexerProperties, List<Argument> arguments, out ValidationError error) 3639 { 3640 List<CandidateMember> candidates = new List<CandidateMember>(); 3641 3642 error = null; 3643 3644 int errorCount = 0; 3645 foreach (PropertyInfo indexerProp in indexerProperties) 3646 { 3647 ValidationError tempError = null; 3648 EvaluateCandidate(candidates, indexerProp, indexerProp.GetIndexParameters(), arguments, out tempError, 3649 delegate(string propName, int numArguments) 3650 { 3651 string message = string.Format(CultureInfo.CurrentCulture, Messages.IndexerCountMismatch, numArguments); 3652 return new ValidationError(message, ErrorNumbers.Error_IndexerCountMismatch); 3653 }); 3654 3655 error = tempError; 3656 if (tempError != null) 3657 ++errorCount; 3658 } 3659 3660 if (candidates.Count == 0) 3661 { 3662 // No candidates were found. 3663 3664 if (errorCount > 1) 3665 { 3666 // If multiple candidates generated errors, then use a more generic error that says 3667 // we couldn't find a matching overload. 3668 string message = string.Format(CultureInfo.CurrentCulture, Messages.IndexerOverloadNotFound); 3669 error = new ValidationError(message, ErrorNumbers.Error_IndexerOverloadNotFound); 3670 } 3671 3672 return null; 3673 } 3674 else 3675 { 3676 // If there are any candidates, then wipe out any errors left over from any mismatches. 3677 error = null; 3678 } 3679 3680 return candidates; 3681 } 3682 3683 #endregion 3684 3685 #region Type resolution 3686 AddTypeReference(CodeTypeReference typeRef, Type type)3687 internal void AddTypeReference(CodeTypeReference typeRef, Type type) 3688 { 3689 typeRefMap[typeRef] = type; 3690 } 3691 ResolveType(CodeTypeReference typeRef)3692 internal Type ResolveType(CodeTypeReference typeRef) 3693 { 3694 Type resultType = null; 3695 3696 if (!typeRefMap.TryGetValue(typeRef, out resultType)) 3697 { 3698 string message; 3699 3700 resultType = FindType(typeRef.BaseType); 3701 3702 if (resultType == null) 3703 { 3704 // check if we have a qualifiedname saved, and if we do, use it 3705 string qualifiedName = typeRef.UserData[RuleUserDataKeys.QualifiedName] as string; 3706 resultType = ResolveType(qualifiedName); 3707 if (resultType != null) 3708 { 3709 // qualified name returned the complete type, save it and we're done 3710 typeRefMap.Add(typeRef, resultType); 3711 return resultType; 3712 } 3713 message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownType, typeRef.BaseType); 3714 ValidationError error = new ValidationError(message, ErrorNumbers.Error_UnableToResolveType); 3715 error.UserData[RuleUserDataKeys.ErrorObject] = typeRef; 3716 Errors.Add(error); 3717 return null; 3718 } 3719 3720 // Handle generic type arguments. 3721 if (typeRef.TypeArguments.Count > 0) 3722 { 3723 Type[] typeArguments = new Type[typeRef.TypeArguments.Count]; 3724 for (int i = 0; i < typeRef.TypeArguments.Count; ++i) 3725 { 3726 // design-time types don't have fully-qualified names, so when they are 3727 // used in a generic CodeTypeReference constructor leaves them with [] 3728 // surrounding them. Remove the [] if possible 3729 CodeTypeReference arg = typeRef.TypeArguments[i]; 3730 if (arg.BaseType.StartsWith("[", StringComparison.Ordinal)) 3731 arg.BaseType = arg.BaseType.Substring(1, arg.BaseType.Length - 2); 3732 3733 typeArguments[i] = ResolveType(arg); 3734 if (typeArguments[i] == null) 3735 return null; 3736 } 3737 3738 resultType = resultType.MakeGenericType(typeArguments); 3739 if (resultType == null) 3740 { 3741 StringBuilder sb = new StringBuilder(typeRef.BaseType); 3742 string prefix = "<"; 3743 foreach (Type t in typeArguments) 3744 { 3745 sb.Append(prefix); 3746 prefix = ","; 3747 sb.Append(RuleDecompiler.DecompileType(t)); 3748 } 3749 sb.Append(">"); 3750 message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownGenericType, sb.ToString()); 3751 ValidationError error = new ValidationError(message, ErrorNumbers.Error_UnableToResolveType); 3752 error.UserData[RuleUserDataKeys.ErrorObject] = typeRef; 3753 Errors.Add(error); 3754 return null; 3755 } 3756 } 3757 3758 3759 if (resultType != null) 3760 { 3761 CodeTypeReference arrayTypeRef = typeRef; 3762 if (arrayTypeRef.ArrayRank > 0) 3763 { 3764 do 3765 { 3766 resultType = (arrayTypeRef.ArrayRank == 1) ? resultType.MakeArrayType() : resultType.MakeArrayType(arrayTypeRef.ArrayRank); 3767 3768 arrayTypeRef = arrayTypeRef.ArrayElementType; 3769 } while (arrayTypeRef.ArrayRank > 0); 3770 } 3771 } 3772 3773 if (resultType != null) 3774 { 3775 typeRefMap.Add(typeRef, resultType); 3776 3777 // at runtime we may not have the assembly loaded, so keep the fully qualified name around 3778 typeRef.UserData[RuleUserDataKeys.QualifiedName] = resultType.AssemblyQualifiedName; 3779 } 3780 } 3781 3782 return resultType; 3783 } 3784 ResolveType(string qualifiedName)3785 internal Type ResolveType(string qualifiedName) 3786 { 3787 Type resultType = null; 3788 if (qualifiedName != null) 3789 { 3790 resultType = typeProvider.GetType(qualifiedName, false); 3791 3792 // if the Typeprovider can't find it, use the framework, 3793 // since it should be an AssemblyQualifiedName 3794 if (resultType == null) 3795 resultType = Type.GetType(qualifiedName, false); 3796 3797 } 3798 return resultType; 3799 } 3800 FindType(string typeName)3801 private Type FindType(string typeName) 3802 { 3803 if (typeName == null) 3804 throw new ArgumentNullException("typeName"); 3805 3806 Type type = null; 3807 3808 // do we know about this type 3809 if (!typesUsed.TryGetValue(typeName, out type)) 3810 { 3811 type = typeProvider.GetType(typeName, false); 3812 3813 if (type != null) 3814 { 3815 typesUsed.Add(typeName, type); 3816 3817 IsAuthorized(type); 3818 } 3819 } 3820 3821 return type; 3822 } 3823 IsAuthorized(Type type)3824 internal void IsAuthorized(Type type) 3825 { 3826 Debug.Assert(!type.IsPointer && !type.IsByRef, 3827 "IsAuthorized should not be called for a type that is a pointer or passed by reference : " + type.AssemblyQualifiedName); 3828 if (checkStaticType) 3829 { 3830 if (authorizedTypes == null) 3831 { 3832 ValidationError error = new ValidationError(Messages.Error_ConfigFileMissingOrInvalid, ErrorNumbers.Error_ConfigFileMissingOrInvalid); 3833 Errors.Add(error); 3834 } 3835 else 3836 { 3837 while (type.IsArray) 3838 { 3839 type = type.GetElementType(); 3840 } 3841 if (type.IsGenericType) 3842 { 3843 IsAuthorizedSimpleType(type.GetGenericTypeDefinition()); 3844 Type[] typeArguments = type.GetGenericArguments(); 3845 foreach (Type t in typeArguments) 3846 { 3847 IsAuthorized(t); 3848 } 3849 } 3850 else 3851 { 3852 IsAuthorizedSimpleType(type); 3853 } 3854 } 3855 } 3856 } 3857 IsAuthorizedSimpleType(Type type)3858 void IsAuthorizedSimpleType(Type type) 3859 { 3860 Debug.Assert((!type.IsGenericType || type.IsGenericTypeDefinition) && !type.HasElementType, 3861 "IsAuthorizedSimpleType should not be called for a partially specialized generic type or a type that encompasses or refers to another type : " + 3862 type.AssemblyQualifiedName); 3863 3864 string qualifiedName = type.AssemblyQualifiedName; 3865 3866 if (!typesUsedAuthorized.ContainsKey(qualifiedName)) 3867 { 3868 bool authorized = false; 3869 foreach (AuthorizedType authorizedType in authorizedTypes) 3870 { 3871 if (authorizedType.RegularExpression.IsMatch(qualifiedName)) 3872 { 3873 authorized = (String.Compare(bool.TrueString, authorizedType.Authorized, StringComparison.OrdinalIgnoreCase) == 0); 3874 if (!authorized) 3875 break; 3876 } 3877 } 3878 if (!authorized) 3879 { 3880 string message = string.Format(CultureInfo.CurrentCulture, Messages.Error_TypeNotAuthorized, type.FullName); 3881 ValidationError error = new ValidationError(message, ErrorNumbers.Error_TypeNotAuthorized); 3882 error.UserData[RuleUserDataKeys.ErrorObject] = type; 3883 Errors.Add(error); 3884 } 3885 else 3886 { 3887 typesUsedAuthorized.Add(qualifiedName, type); 3888 } 3889 } 3890 } 3891 3892 #endregion 3893 3894 #endregion 3895 } 3896 #endregion RuleValidator 3897 } 3898