1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Diagnostics; 6 using System.Dynamic.Utils; 7 using System.Reflection; 8 using System.Reflection.Emit; 9 using static System.Linq.Expressions.CachedReflectionInfo; 10 11 namespace System.Linq.Expressions.Compiler 12 { 13 internal static class ILGen 14 { Emit(this ILGenerator il, OpCode opcode, MethodBase methodBase)15 internal static void Emit(this ILGenerator il, OpCode opcode, MethodBase methodBase) 16 { 17 Debug.Assert(methodBase is MethodInfo || methodBase is ConstructorInfo); 18 19 var ctor = methodBase as ConstructorInfo; 20 if ((object)ctor != null) 21 { 22 il.Emit(opcode, ctor); 23 } 24 else 25 { 26 il.Emit(opcode, (MethodInfo)methodBase); 27 } 28 } 29 30 #region Instruction helpers 31 EmitLoadArg(this ILGenerator il, int index)32 internal static void EmitLoadArg(this ILGenerator il, int index) 33 { 34 Debug.Assert(index >= 0); 35 Debug.Assert(index < ushort.MaxValue); 36 37 switch (index) 38 { 39 case 0: 40 il.Emit(OpCodes.Ldarg_0); 41 break; 42 case 1: 43 il.Emit(OpCodes.Ldarg_1); 44 break; 45 case 2: 46 il.Emit(OpCodes.Ldarg_2); 47 break; 48 case 3: 49 il.Emit(OpCodes.Ldarg_3); 50 break; 51 default: 52 if (index <= byte.MaxValue) 53 { 54 il.Emit(OpCodes.Ldarg_S, (byte)index); 55 } 56 else 57 { 58 // cast to short, result is correct ushort. 59 il.Emit(OpCodes.Ldarg, (short)index); 60 } 61 break; 62 } 63 } 64 EmitLoadArgAddress(this ILGenerator il, int index)65 internal static void EmitLoadArgAddress(this ILGenerator il, int index) 66 { 67 Debug.Assert(index >= 0); 68 Debug.Assert(index < ushort.MaxValue); 69 70 if (index <= byte.MaxValue) 71 { 72 il.Emit(OpCodes.Ldarga_S, (byte)index); 73 } 74 else 75 { 76 // cast to short, result is correct ushort. 77 il.Emit(OpCodes.Ldarga, (short)index); 78 } 79 } 80 EmitStoreArg(this ILGenerator il, int index)81 internal static void EmitStoreArg(this ILGenerator il, int index) 82 { 83 Debug.Assert(index >= 0); 84 Debug.Assert(index < ushort.MaxValue); 85 86 if (index <= byte.MaxValue) 87 { 88 il.Emit(OpCodes.Starg_S, (byte)index); 89 } 90 else 91 { 92 // cast to short, result is correct ushort. 93 il.Emit(OpCodes.Starg, (short)index); 94 } 95 } 96 97 /// <summary> 98 /// Emits a Ldind* instruction for the appropriate type 99 /// </summary> EmitLoadValueIndirect(this ILGenerator il, Type type)100 internal static void EmitLoadValueIndirect(this ILGenerator il, Type type) 101 { 102 Debug.Assert(type != null); 103 104 switch (type.GetTypeCode()) 105 { 106 case TypeCode.Byte: 107 il.Emit(OpCodes.Ldind_I1); 108 break; 109 case TypeCode.Boolean: 110 case TypeCode.SByte: 111 il.Emit(OpCodes.Ldind_U1); 112 break; 113 case TypeCode.Int16: 114 il.Emit(OpCodes.Ldind_I2); 115 break; 116 case TypeCode.Char: 117 case TypeCode.UInt16: 118 il.Emit(OpCodes.Ldind_U2); 119 break; 120 case TypeCode.Int32: 121 il.Emit(OpCodes.Ldind_I4); 122 break; 123 case TypeCode.UInt32: 124 il.Emit(OpCodes.Ldind_U4); 125 break; 126 case TypeCode.Int64: 127 case TypeCode.UInt64: 128 il.Emit(OpCodes.Ldind_I8); 129 break; 130 case TypeCode.Single: 131 il.Emit(OpCodes.Ldind_R4); 132 break; 133 case TypeCode.Double: 134 il.Emit(OpCodes.Ldind_R8); 135 break; 136 default: 137 if (type.IsValueType) 138 { 139 il.Emit(OpCodes.Ldobj, type); 140 } 141 else 142 { 143 il.Emit(OpCodes.Ldind_Ref); 144 } 145 break; 146 } 147 } 148 149 150 /// <summary> 151 /// Emits a Stind* instruction for the appropriate type. 152 /// </summary> EmitStoreValueIndirect(this ILGenerator il, Type type)153 internal static void EmitStoreValueIndirect(this ILGenerator il, Type type) 154 { 155 Debug.Assert(type != null); 156 157 switch (type.GetTypeCode()) 158 { 159 case TypeCode.Boolean: 160 case TypeCode.Byte: 161 case TypeCode.SByte: 162 il.Emit(OpCodes.Stind_I1); 163 break; 164 case TypeCode.Char: 165 case TypeCode.Int16: 166 case TypeCode.UInt16: 167 il.Emit(OpCodes.Stind_I2); 168 break; 169 case TypeCode.Int32: 170 case TypeCode.UInt32: 171 il.Emit(OpCodes.Stind_I4); 172 break; 173 case TypeCode.Int64: 174 case TypeCode.UInt64: 175 il.Emit(OpCodes.Stind_I8); 176 break; 177 case TypeCode.Single: 178 il.Emit(OpCodes.Stind_R4); 179 break; 180 case TypeCode.Double: 181 il.Emit(OpCodes.Stind_R8); 182 break; 183 default: 184 if (type.IsValueType) 185 { 186 il.Emit(OpCodes.Stobj, type); 187 } 188 else 189 { 190 il.Emit(OpCodes.Stind_Ref); 191 } 192 break; 193 } 194 } 195 196 // Emits the Ldelem* instruction for the appropriate type 197 EmitLoadElement(this ILGenerator il, Type type)198 internal static void EmitLoadElement(this ILGenerator il, Type type) 199 { 200 Debug.Assert(type != null); 201 202 if (!type.IsValueType) 203 { 204 il.Emit(OpCodes.Ldelem_Ref); 205 } 206 else 207 { 208 switch (type.GetTypeCode()) 209 { 210 case TypeCode.Boolean: 211 case TypeCode.SByte: 212 il.Emit(OpCodes.Ldelem_I1); 213 break; 214 case TypeCode.Byte: 215 il.Emit(OpCodes.Ldelem_U1); 216 break; 217 case TypeCode.Int16: 218 il.Emit(OpCodes.Ldelem_I2); 219 break; 220 case TypeCode.Char: 221 case TypeCode.UInt16: 222 il.Emit(OpCodes.Ldelem_U2); 223 break; 224 case TypeCode.Int32: 225 il.Emit(OpCodes.Ldelem_I4); 226 break; 227 case TypeCode.UInt32: 228 il.Emit(OpCodes.Ldelem_U4); 229 break; 230 case TypeCode.Int64: 231 case TypeCode.UInt64: 232 il.Emit(OpCodes.Ldelem_I8); 233 break; 234 case TypeCode.Single: 235 il.Emit(OpCodes.Ldelem_R4); 236 break; 237 case TypeCode.Double: 238 il.Emit(OpCodes.Ldelem_R8); 239 break; 240 default: 241 il.Emit(OpCodes.Ldelem, type); 242 break; 243 } 244 } 245 } 246 247 /// <summary> 248 /// Emits a Stelem* instruction for the appropriate type. 249 /// </summary> EmitStoreElement(this ILGenerator il, Type type)250 internal static void EmitStoreElement(this ILGenerator il, Type type) 251 { 252 Debug.Assert(type != null); 253 254 switch (type.GetTypeCode()) 255 { 256 case TypeCode.Boolean: 257 case TypeCode.SByte: 258 case TypeCode.Byte: 259 il.Emit(OpCodes.Stelem_I1); 260 break; 261 case TypeCode.Char: 262 case TypeCode.Int16: 263 case TypeCode.UInt16: 264 il.Emit(OpCodes.Stelem_I2); 265 break; 266 case TypeCode.Int32: 267 case TypeCode.UInt32: 268 il.Emit(OpCodes.Stelem_I4); 269 break; 270 case TypeCode.Int64: 271 case TypeCode.UInt64: 272 il.Emit(OpCodes.Stelem_I8); 273 break; 274 case TypeCode.Single: 275 il.Emit(OpCodes.Stelem_R4); 276 break; 277 case TypeCode.Double: 278 il.Emit(OpCodes.Stelem_R8); 279 break; 280 default: 281 if (type.IsValueType) 282 { 283 il.Emit(OpCodes.Stelem, type); 284 } 285 else 286 { 287 il.Emit(OpCodes.Stelem_Ref); 288 } 289 break; 290 } 291 } 292 EmitType(this ILGenerator il, Type type)293 internal static void EmitType(this ILGenerator il, Type type) 294 { 295 Debug.Assert(type != null); 296 297 il.Emit(OpCodes.Ldtoken, type); 298 il.Emit(OpCodes.Call, Type_GetTypeFromHandle); 299 } 300 301 #endregion 302 303 #region Fields, properties and methods 304 EmitFieldAddress(this ILGenerator il, FieldInfo fi)305 internal static void EmitFieldAddress(this ILGenerator il, FieldInfo fi) 306 { 307 Debug.Assert(fi != null); 308 309 il.Emit(fi.IsStatic ? OpCodes.Ldsflda : OpCodes.Ldflda, fi); 310 } 311 EmitFieldGet(this ILGenerator il, FieldInfo fi)312 internal static void EmitFieldGet(this ILGenerator il, FieldInfo fi) 313 { 314 Debug.Assert(fi != null); 315 316 il.Emit(fi.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, fi); 317 } 318 EmitFieldSet(this ILGenerator il, FieldInfo fi)319 internal static void EmitFieldSet(this ILGenerator il, FieldInfo fi) 320 { 321 Debug.Assert(fi != null); 322 323 il.Emit(fi.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fi); 324 } 325 326 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")] EmitNew(this ILGenerator il, ConstructorInfo ci)327 internal static void EmitNew(this ILGenerator il, ConstructorInfo ci) 328 { 329 Debug.Assert(ci != null); 330 Debug.Assert(!ci.DeclaringType.ContainsGenericParameters); 331 332 il.Emit(OpCodes.Newobj, ci); 333 } 334 335 #endregion 336 337 #region Constants 338 EmitNull(this ILGenerator il)339 internal static void EmitNull(this ILGenerator il) 340 { 341 il.Emit(OpCodes.Ldnull); 342 } 343 EmitString(this ILGenerator il, string value)344 internal static void EmitString(this ILGenerator il, string value) 345 { 346 Debug.Assert(value != null); 347 348 il.Emit(OpCodes.Ldstr, value); 349 } 350 EmitPrimitive(this ILGenerator il, bool value)351 internal static void EmitPrimitive(this ILGenerator il, bool value) 352 { 353 il.Emit(value ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); 354 } 355 EmitPrimitive(this ILGenerator il, int value)356 internal static void EmitPrimitive(this ILGenerator il, int value) 357 { 358 OpCode c; 359 switch (value) 360 { 361 case -1: 362 c = OpCodes.Ldc_I4_M1; 363 break; 364 case 0: 365 c = OpCodes.Ldc_I4_0; 366 break; 367 case 1: 368 c = OpCodes.Ldc_I4_1; 369 break; 370 case 2: 371 c = OpCodes.Ldc_I4_2; 372 break; 373 case 3: 374 c = OpCodes.Ldc_I4_3; 375 break; 376 case 4: 377 c = OpCodes.Ldc_I4_4; 378 break; 379 case 5: 380 c = OpCodes.Ldc_I4_5; 381 break; 382 case 6: 383 c = OpCodes.Ldc_I4_6; 384 break; 385 case 7: 386 c = OpCodes.Ldc_I4_7; 387 break; 388 case 8: 389 c = OpCodes.Ldc_I4_8; 390 break; 391 default: 392 if (value >= sbyte.MinValue && value <= sbyte.MaxValue) 393 { 394 il.Emit(OpCodes.Ldc_I4_S, (sbyte)value); 395 } 396 else 397 { 398 il.Emit(OpCodes.Ldc_I4, value); 399 } 400 return; 401 } 402 il.Emit(c); 403 } 404 EmitPrimitive(this ILGenerator il, uint value)405 private static void EmitPrimitive(this ILGenerator il, uint value) 406 { 407 il.EmitPrimitive(unchecked((int)value)); 408 } 409 EmitPrimitive(this ILGenerator il, long value)410 private static void EmitPrimitive(this ILGenerator il, long value) 411 { 412 if (int.MinValue <= value & value <= uint.MaxValue) 413 { 414 il.EmitPrimitive(unchecked((int)value)); 415 // While often not of consequence depending on what follows, there are cases where this 416 // casting matters. Values [0, int.MaxValue] can use either safely, but negative values 417 // must use conv.i8 and those (int.MaxValue, uint.MaxValue] must use conv.u8, or else 418 // the higher bits will be wrong. 419 il.Emit(value > 0 ? OpCodes.Conv_U8 : OpCodes.Conv_I8); 420 } 421 else 422 { 423 il.Emit(OpCodes.Ldc_I8, value); 424 } 425 } 426 EmitPrimitive(this ILGenerator il, ulong value)427 private static void EmitPrimitive(this ILGenerator il, ulong value) 428 { 429 il.EmitPrimitive(unchecked((long)value)); 430 } 431 EmitPrimitive(this ILGenerator il, double value)432 private static void EmitPrimitive(this ILGenerator il, double value) 433 { 434 il.Emit(OpCodes.Ldc_R8, value); 435 } 436 EmitPrimitive(this ILGenerator il, float value)437 private static void EmitPrimitive(this ILGenerator il, float value) 438 { 439 il.Emit(OpCodes.Ldc_R4, value); 440 } 441 442 // matches TryEmitConstant CanEmitConstant(object value, Type type)443 internal static bool CanEmitConstant(object value, Type type) 444 { 445 if (value == null || CanEmitILConstant(type)) 446 { 447 return true; 448 } 449 450 Type t = value as Type; 451 if (t != null) 452 { 453 return ShouldLdtoken(t); 454 } 455 456 MethodBase mb = value as MethodBase; 457 return mb != null && ShouldLdtoken(mb); 458 } 459 460 // matches TryEmitILConstant CanEmitILConstant(Type type)461 private static bool CanEmitILConstant(Type type) 462 { 463 switch (type.GetNonNullableType().GetTypeCode()) 464 { 465 case TypeCode.Boolean: 466 case TypeCode.SByte: 467 case TypeCode.Int16: 468 case TypeCode.Int32: 469 case TypeCode.Int64: 470 case TypeCode.Single: 471 case TypeCode.Double: 472 case TypeCode.Char: 473 case TypeCode.Byte: 474 case TypeCode.UInt16: 475 case TypeCode.UInt32: 476 case TypeCode.UInt64: 477 case TypeCode.Decimal: 478 case TypeCode.String: 479 return true; 480 } 481 482 return false; 483 } 484 485 // 486 // Note: we support emitting more things as IL constants than 487 // Linq does TryEmitConstant(this ILGenerator il, object value, Type type, ILocalCache locals)488 internal static bool TryEmitConstant(this ILGenerator il, object value, Type type, ILocalCache locals) 489 { 490 if (value == null) 491 { 492 // Smarter than the Linq implementation which uses the initobj 493 // pattern for all value types (works, but requires a local and 494 // more IL) 495 il.EmitDefault(type, locals); 496 return true; 497 } 498 499 // Handle the easy cases 500 if (il.TryEmitILConstant(value, type)) 501 { 502 return true; 503 } 504 505 // Check for a few more types that we support emitting as constants 506 Type t = value as Type; 507 if (t != null) 508 { 509 if (ShouldLdtoken(t)) 510 { 511 il.EmitType(t); 512 if (type != typeof(Type)) 513 { 514 il.Emit(OpCodes.Castclass, type); 515 } 516 517 return true; 518 } 519 520 return false; 521 } 522 523 MethodBase mb = value as MethodBase; 524 if (mb != null && ShouldLdtoken(mb)) 525 { 526 il.Emit(OpCodes.Ldtoken, mb); 527 Type dt = mb.DeclaringType; 528 if (dt != null && dt.IsGenericType) 529 { 530 il.Emit(OpCodes.Ldtoken, dt); 531 il.Emit(OpCodes.Call, MethodBase_GetMethodFromHandle_RuntimeMethodHandle_RuntimeTypeHandle); 532 } 533 else 534 { 535 il.Emit(OpCodes.Call, MethodBase_GetMethodFromHandle_RuntimeMethodHandle); 536 } 537 538 if (type != typeof(MethodBase)) 539 { 540 il.Emit(OpCodes.Castclass, type); 541 } 542 543 return true; 544 } 545 546 return false; 547 } 548 ShouldLdtoken(Type t)549 private static bool ShouldLdtoken(Type t) 550 { 551 // If CompileToMethod is re-enabled, t is TypeBuilder should also return 552 // true when not compiling to a DynamicMethod 553 return t.IsGenericParameter || t.IsVisible; 554 } 555 ShouldLdtoken(MethodBase mb)556 internal static bool ShouldLdtoken(MethodBase mb) 557 { 558 // Can't ldtoken on a DynamicMethod 559 if (mb is DynamicMethod) 560 { 561 return false; 562 } 563 564 Type dt = mb.DeclaringType; 565 return dt == null || ShouldLdtoken(dt); 566 } 567 TryEmitILConstant(this ILGenerator il, object value, Type type)568 private static bool TryEmitILConstant(this ILGenerator il, object value, Type type) 569 { 570 Debug.Assert(value != null); 571 572 if (type.IsNullableType()) 573 { 574 Type nonNullType = type.GetNonNullableType(); 575 576 if (TryEmitILConstant(il, value, nonNullType)) 577 { 578 il.Emit(OpCodes.Newobj, type.GetConstructor(new[] { nonNullType })); 579 return true; 580 } 581 582 return false; 583 } 584 585 switch (type.GetTypeCode()) 586 { 587 case TypeCode.Boolean: 588 il.EmitPrimitive((bool)value); 589 return true; 590 case TypeCode.SByte: 591 il.EmitPrimitive((sbyte)value); 592 return true; 593 case TypeCode.Int16: 594 il.EmitPrimitive((short)value); 595 return true; 596 case TypeCode.Int32: 597 il.EmitPrimitive((int)value); 598 return true; 599 case TypeCode.Int64: 600 il.EmitPrimitive((long)value); 601 return true; 602 case TypeCode.Single: 603 il.EmitPrimitive((float)value); 604 return true; 605 case TypeCode.Double: 606 il.EmitPrimitive((double)value); 607 return true; 608 case TypeCode.Char: 609 il.EmitPrimitive((char)value); 610 return true; 611 case TypeCode.Byte: 612 il.EmitPrimitive((byte)value); 613 return true; 614 case TypeCode.UInt16: 615 il.EmitPrimitive((ushort)value); 616 return true; 617 case TypeCode.UInt32: 618 il.EmitPrimitive((uint)value); 619 return true; 620 case TypeCode.UInt64: 621 il.EmitPrimitive((ulong)value); 622 return true; 623 case TypeCode.Decimal: 624 il.EmitDecimal((decimal)value); 625 return true; 626 case TypeCode.String: 627 il.EmitString((string)value); 628 return true; 629 default: 630 return false; 631 } 632 } 633 634 #endregion 635 636 #region Linq Conversions 637 EmitConvertToType(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)638 internal static void EmitConvertToType(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals) 639 { 640 if (TypeUtils.AreEquivalent(typeFrom, typeTo)) 641 { 642 return; 643 } 644 645 Debug.Assert(typeFrom != typeof(void) && typeTo != typeof(void)); 646 647 bool isTypeFromNullable = typeFrom.IsNullableType(); 648 bool isTypeToNullable = typeTo.IsNullableType(); 649 650 Type nnExprType = typeFrom.GetNonNullableType(); 651 Type nnType = typeTo.GetNonNullableType(); 652 653 if (typeFrom.IsInterface || // interface cast 654 typeTo.IsInterface || 655 typeFrom == typeof(object) || // boxing cast 656 typeTo == typeof(object) || 657 typeFrom == typeof(System.Enum) || 658 typeFrom == typeof(System.ValueType) || 659 TypeUtils.IsLegalExplicitVariantDelegateConversion(typeFrom, typeTo)) 660 { 661 il.EmitCastToType(typeFrom, typeTo); 662 } 663 else if (isTypeFromNullable || isTypeToNullable) 664 { 665 il.EmitNullableConversion(typeFrom, typeTo, isChecked, locals); 666 } 667 else if (!(typeFrom.IsConvertible() && typeTo.IsConvertible()) // primitive runtime conversion 668 && 669 (nnExprType.IsAssignableFrom(nnType) || // down cast 670 nnType.IsAssignableFrom(nnExprType))) // up cast 671 { 672 il.EmitCastToType(typeFrom, typeTo); 673 } 674 else if (typeFrom.IsArray && typeTo.IsArray) 675 { 676 // See DevDiv Bugs #94657. 677 il.EmitCastToType(typeFrom, typeTo); 678 } 679 else 680 { 681 il.EmitNumericConversion(typeFrom, typeTo, isChecked); 682 } 683 } 684 685 EmitCastToType(this ILGenerator il, Type typeFrom, Type typeTo)686 private static void EmitCastToType(this ILGenerator il, Type typeFrom, Type typeTo) 687 { 688 if (typeFrom.IsValueType) 689 { 690 Debug.Assert(!typeTo.IsValueType); 691 il.Emit(OpCodes.Box, typeFrom); 692 if (typeTo != typeof(object)) 693 { 694 il.Emit(OpCodes.Castclass, typeTo); 695 } 696 } 697 else 698 { 699 il.Emit(typeTo.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, typeTo); 700 } 701 } 702 703 704 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] EmitNumericConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked)705 private static void EmitNumericConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) 706 { 707 TypeCode tc = typeTo.GetTypeCode(); 708 TypeCode tf = typeFrom.GetTypeCode(); 709 710 if (tc == tf) 711 { 712 // Between enums of same underlying type, or between such an enum and the underlying type itself. 713 // Includes bool-backed enums, which is the only valid conversion to or from bool. 714 // Just leave the value on the stack, and treat it as the wanted type. 715 return; 716 } 717 718 bool isFromUnsigned = tf.IsUnsigned(); 719 OpCode convCode; 720 switch (tc) 721 { 722 case TypeCode.Single: 723 if (isFromUnsigned) 724 il.Emit(OpCodes.Conv_R_Un); 725 convCode = OpCodes.Conv_R4; 726 break; 727 case TypeCode.Double: 728 if (isFromUnsigned) 729 il.Emit(OpCodes.Conv_R_Un); 730 convCode = OpCodes.Conv_R8; 731 break; 732 case TypeCode.Decimal: 733 734 // NB: TypeUtils.IsImplicitNumericConversion makes the promise that implicit conversions 735 // from various integral types and char to decimal are possible. Coalesce allows the 736 // conversion lambda to be omitted in these cases, so we have to handle this case in 737 // here as well, by using the op_Implicit operator implementation on System.Decimal 738 // because there are no opcodes for System.Decimal. 739 740 Debug.Assert(typeFrom != typeTo); 741 742 MethodInfo method; 743 744 switch (tf) 745 { 746 case TypeCode.Byte: method = Decimal_op_Implicit_Byte; break; 747 case TypeCode.SByte: method = Decimal_op_Implicit_SByte; break; 748 case TypeCode.Int16: method = Decimal_op_Implicit_Int16; break; 749 case TypeCode.UInt16: method = Decimal_op_Implicit_UInt16; break; 750 case TypeCode.Int32: method = Decimal_op_Implicit_Int32; break; 751 case TypeCode.UInt32: method = Decimal_op_Implicit_UInt32; break; 752 case TypeCode.Int64: method = Decimal_op_Implicit_Int64; break; 753 case TypeCode.UInt64: method = Decimal_op_Implicit_UInt64; break; 754 case TypeCode.Char: method = Decimal_op_Implicit_Char; break; 755 default: 756 throw ContractUtils.Unreachable; 757 } 758 759 il.Emit(OpCodes.Call, method); 760 return; 761 case TypeCode.SByte: 762 if (isChecked) 763 { 764 convCode = isFromUnsigned ? OpCodes.Conv_Ovf_I1_Un : OpCodes.Conv_Ovf_I1; 765 } 766 else 767 { 768 if (tf == TypeCode.Byte) 769 { 770 return; 771 } 772 773 convCode = OpCodes.Conv_I1; 774 } 775 776 break; 777 case TypeCode.Byte: 778 if (isChecked) 779 { 780 convCode = isFromUnsigned ? OpCodes.Conv_Ovf_U1_Un : OpCodes.Conv_Ovf_U1; 781 } 782 else 783 { 784 if (tf == TypeCode.SByte) 785 { 786 return; 787 } 788 789 convCode = OpCodes.Conv_U1; 790 } 791 792 break; 793 case TypeCode.Int16: 794 switch (tf) 795 { 796 case TypeCode.SByte: 797 case TypeCode.Byte: 798 return; 799 case TypeCode.Char: 800 case TypeCode.UInt16: 801 if (!isChecked) 802 { 803 return; 804 } 805 806 break; 807 } 808 809 convCode = isChecked 810 ? (isFromUnsigned ? OpCodes.Conv_Ovf_I2_Un : OpCodes.Conv_Ovf_I2) 811 : OpCodes.Conv_I2; 812 break; 813 case TypeCode.Char: 814 case TypeCode.UInt16: 815 switch (tf) 816 { 817 case TypeCode.Byte: 818 case TypeCode.Char: 819 case TypeCode.UInt16: 820 return; 821 case TypeCode.SByte: 822 case TypeCode.Int16: 823 if (!isChecked) 824 { 825 return; 826 } 827 828 break; 829 } 830 831 convCode = isChecked 832 ? (isFromUnsigned ? OpCodes.Conv_Ovf_U2_Un : OpCodes.Conv_Ovf_U2) 833 : OpCodes.Conv_U2; 834 break; 835 case TypeCode.Int32: 836 switch (tf) 837 { 838 case TypeCode.Byte: 839 case TypeCode.SByte: 840 case TypeCode.Int16: 841 case TypeCode.UInt16: 842 return; 843 case TypeCode.UInt32: 844 if (!isChecked) 845 { 846 return; 847 } 848 849 break; 850 } 851 852 convCode = isChecked 853 ? (isFromUnsigned ? OpCodes.Conv_Ovf_I4_Un : OpCodes.Conv_Ovf_I4) 854 : OpCodes.Conv_I4; 855 break; 856 case TypeCode.UInt32: 857 switch (tf) 858 { 859 case TypeCode.Byte: 860 case TypeCode.Char: 861 case TypeCode.UInt16: 862 return; 863 case TypeCode.SByte: 864 case TypeCode.Int16: 865 case TypeCode.Int32: 866 if (!isChecked) 867 { 868 return; 869 } 870 871 break; 872 } 873 874 convCode = isChecked 875 ? (isFromUnsigned ? OpCodes.Conv_Ovf_U4_Un : OpCodes.Conv_Ovf_U4) 876 : OpCodes.Conv_U4; 877 break; 878 case TypeCode.Int64: 879 if (!isChecked && tf == TypeCode.UInt64) 880 { 881 return; 882 } 883 884 convCode = isChecked 885 ? (isFromUnsigned ? OpCodes.Conv_Ovf_I8_Un : OpCodes.Conv_Ovf_I8) 886 : (isFromUnsigned ? OpCodes.Conv_U8 : OpCodes.Conv_I8); 887 break; 888 case TypeCode.UInt64: 889 if (!isChecked && tf == TypeCode.Int64) 890 { 891 return; 892 } 893 894 convCode = isChecked 895 ? (isFromUnsigned || tf.IsFloatingPoint() ? OpCodes.Conv_Ovf_U8_Un : OpCodes.Conv_Ovf_U8) 896 : (isFromUnsigned || tf.IsFloatingPoint() ? OpCodes.Conv_U8 : OpCodes.Conv_I8); 897 break; 898 default: 899 throw ContractUtils.Unreachable; 900 } 901 902 il.Emit(convCode); 903 } 904 EmitNullableToNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)905 private static void EmitNullableToNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals) 906 { 907 Debug.Assert(typeFrom.IsNullableType()); 908 Debug.Assert(typeTo.IsNullableType()); 909 Label labIfNull; 910 Label labEnd; 911 LocalBuilder locFrom = locals.GetLocal(typeFrom); 912 il.Emit(OpCodes.Stloc, locFrom); 913 // test for null 914 il.Emit(OpCodes.Ldloca, locFrom); 915 il.EmitHasValue(typeFrom); 916 labIfNull = il.DefineLabel(); 917 il.Emit(OpCodes.Brfalse_S, labIfNull); 918 il.Emit(OpCodes.Ldloca, locFrom); 919 locals.FreeLocal(locFrom); 920 il.EmitGetValueOrDefault(typeFrom); 921 Type nnTypeFrom = typeFrom.GetNonNullableType(); 922 Type nnTypeTo = typeTo.GetNonNullableType(); 923 il.EmitConvertToType(nnTypeFrom, nnTypeTo, isChecked, locals); 924 // construct result type 925 ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo }); 926 il.Emit(OpCodes.Newobj, ci); 927 labEnd = il.DefineLabel(); 928 il.Emit(OpCodes.Br_S, labEnd); 929 // if null then create a default one 930 il.MarkLabel(labIfNull); 931 LocalBuilder locTo = locals.GetLocal(typeTo); 932 il.Emit(OpCodes.Ldloca, locTo); 933 il.Emit(OpCodes.Initobj, typeTo); 934 il.Emit(OpCodes.Ldloc, locTo); 935 locals.FreeLocal(locTo); 936 il.MarkLabel(labEnd); 937 } 938 939 EmitNonNullableToNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)940 private static void EmitNonNullableToNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals) 941 { 942 Debug.Assert(!typeFrom.IsNullableType()); 943 Debug.Assert(typeTo.IsNullableType()); 944 Type nnTypeTo = typeTo.GetNonNullableType(); 945 il.EmitConvertToType(typeFrom, nnTypeTo, isChecked, locals); 946 ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo }); 947 il.Emit(OpCodes.Newobj, ci); 948 } 949 950 EmitNullableToNonNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)951 private static void EmitNullableToNonNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals) 952 { 953 Debug.Assert(typeFrom.IsNullableType()); 954 Debug.Assert(!typeTo.IsNullableType()); 955 if (typeTo.IsValueType) 956 il.EmitNullableToNonNullableStructConversion(typeFrom, typeTo, isChecked, locals); 957 else 958 il.EmitNullableToReferenceConversion(typeFrom); 959 } 960 961 EmitNullableToNonNullableStructConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)962 private static void EmitNullableToNonNullableStructConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals) 963 { 964 Debug.Assert(typeFrom.IsNullableType()); 965 Debug.Assert(!typeTo.IsNullableType()); 966 Debug.Assert(typeTo.IsValueType); 967 LocalBuilder locFrom = locals.GetLocal(typeFrom); 968 il.Emit(OpCodes.Stloc, locFrom); 969 il.Emit(OpCodes.Ldloca, locFrom); 970 locals.FreeLocal(locFrom); 971 il.EmitGetValue(typeFrom); 972 Type nnTypeFrom = typeFrom.GetNonNullableType(); 973 il.EmitConvertToType(nnTypeFrom, typeTo, isChecked, locals); 974 } 975 976 EmitNullableToReferenceConversion(this ILGenerator il, Type typeFrom)977 private static void EmitNullableToReferenceConversion(this ILGenerator il, Type typeFrom) 978 { 979 Debug.Assert(typeFrom.IsNullableType()); 980 // We've got a conversion from nullable to Object, ValueType, Enum, etc. Just box it so that 981 // we get the nullable semantics. 982 il.Emit(OpCodes.Box, typeFrom); 983 } 984 985 EmitNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals)986 private static void EmitNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked, ILocalCache locals) 987 { 988 bool isTypeFromNullable = typeFrom.IsNullableType(); 989 bool isTypeToNullable = typeTo.IsNullableType(); 990 Debug.Assert(isTypeFromNullable || isTypeToNullable); 991 if (isTypeFromNullable && isTypeToNullable) 992 il.EmitNullableToNullableConversion(typeFrom, typeTo, isChecked, locals); 993 else if (isTypeFromNullable) 994 il.EmitNullableToNonNullableConversion(typeFrom, typeTo, isChecked, locals); 995 else 996 il.EmitNonNullableToNullableConversion(typeFrom, typeTo, isChecked, locals); 997 } 998 999 EmitHasValue(this ILGenerator il, Type nullableType)1000 internal static void EmitHasValue(this ILGenerator il, Type nullableType) 1001 { 1002 MethodInfo mi = nullableType.GetMethod("get_HasValue", BindingFlags.Instance | BindingFlags.Public); 1003 Debug.Assert(nullableType.IsValueType); 1004 il.Emit(OpCodes.Call, mi); 1005 } 1006 1007 EmitGetValue(this ILGenerator il, Type nullableType)1008 internal static void EmitGetValue(this ILGenerator il, Type nullableType) 1009 { 1010 MethodInfo mi = nullableType.GetMethod("get_Value", BindingFlags.Instance | BindingFlags.Public); 1011 Debug.Assert(nullableType.IsValueType); 1012 il.Emit(OpCodes.Call, mi); 1013 } 1014 1015 EmitGetValueOrDefault(this ILGenerator il, Type nullableType)1016 internal static void EmitGetValueOrDefault(this ILGenerator il, Type nullableType) 1017 { 1018 MethodInfo mi = nullableType.GetMethod("GetValueOrDefault", System.Type.EmptyTypes); 1019 Debug.Assert(nullableType.IsValueType); 1020 il.Emit(OpCodes.Call, mi); 1021 } 1022 1023 #endregion 1024 1025 #region Arrays 1026 1027 #if FEATURE_COMPILE_TO_METHODBUILDER 1028 /// <summary> 1029 /// Emits an array of constant values provided in the given array. 1030 /// The array is strongly typed. 1031 /// </summary> EmitArray(this ILGenerator il, T[] items, ILocalCache locals)1032 internal static void EmitArray<T>(this ILGenerator il, T[] items, ILocalCache locals) 1033 { 1034 Debug.Assert(items != null); 1035 1036 il.EmitPrimitive(items.Length); 1037 il.Emit(OpCodes.Newarr, typeof(T)); 1038 for (int i = 0; i < items.Length; i++) 1039 { 1040 il.Emit(OpCodes.Dup); 1041 il.EmitPrimitive(i); 1042 il.TryEmitConstant(items[i], typeof(T), locals); 1043 il.EmitStoreElement(typeof(T)); 1044 } 1045 } 1046 #endif 1047 1048 /// <summary> 1049 /// Emits an array of values of count size. 1050 /// </summary> EmitArray(this ILGenerator il, Type elementType, int count)1051 internal static void EmitArray(this ILGenerator il, Type elementType, int count) 1052 { 1053 Debug.Assert(elementType != null); 1054 Debug.Assert(count >= 0); 1055 1056 il.EmitPrimitive(count); 1057 il.Emit(OpCodes.Newarr, elementType); 1058 } 1059 1060 /// <summary> 1061 /// Emits an array construction code. 1062 /// The code assumes that bounds for all dimensions 1063 /// are already emitted. 1064 /// </summary> EmitArray(this ILGenerator il, Type arrayType)1065 internal static void EmitArray(this ILGenerator il, Type arrayType) 1066 { 1067 Debug.Assert(arrayType != null); 1068 Debug.Assert(arrayType.IsArray); 1069 1070 if (arrayType.IsSZArray) 1071 { 1072 il.Emit(OpCodes.Newarr, arrayType.GetElementType()); 1073 } 1074 else 1075 { 1076 Type[] types = new Type[arrayType.GetArrayRank()]; 1077 for (int i = 0; i < types.Length; i++) 1078 { 1079 types[i] = typeof(int); 1080 } 1081 ConstructorInfo ci = arrayType.GetConstructor(types); 1082 Debug.Assert(ci != null); 1083 il.EmitNew(ci); 1084 } 1085 } 1086 1087 #endregion 1088 1089 #region Support for emitting constants 1090 EmitDecimal(this ILGenerator il, decimal value)1091 private static void EmitDecimal(this ILGenerator il, decimal value) 1092 { 1093 int[] bits = decimal.GetBits(value); 1094 int scale = (bits[3] & int.MaxValue) >> 16; 1095 if (scale == 0) 1096 { 1097 if (int.MinValue <= value) 1098 { 1099 if (value <= int.MaxValue) 1100 { 1101 int intValue = decimal.ToInt32(value); 1102 switch (intValue) 1103 { 1104 case -1: 1105 il.Emit(OpCodes.Ldsfld, Decimal_MinusOne); 1106 return; 1107 case 0: 1108 il.EmitDefault(typeof(decimal), locals: null); // locals won't be used. 1109 return; 1110 case 1: 1111 il.Emit(OpCodes.Ldsfld, Decimal_One); 1112 return; 1113 default: 1114 il.EmitPrimitive(intValue); 1115 il.EmitNew(Decimal_Ctor_Int32); 1116 return; 1117 } 1118 } 1119 1120 if (value <= uint.MaxValue) 1121 { 1122 il.EmitPrimitive(decimal.ToUInt32(value)); 1123 il.EmitNew(Decimal_Ctor_UInt32); 1124 return; 1125 } 1126 } 1127 1128 if (long.MinValue <= value) 1129 { 1130 if (value <= long.MaxValue) 1131 { 1132 il.EmitPrimitive(decimal.ToInt64(value)); 1133 il.EmitNew(Decimal_Ctor_Int64); 1134 return; 1135 } 1136 1137 if (value <= ulong.MaxValue) 1138 { 1139 il.EmitPrimitive(decimal.ToUInt64(value)); 1140 il.EmitNew(Decimal_Ctor_UInt64); 1141 return; 1142 } 1143 1144 if (value == decimal.MaxValue) 1145 { 1146 il.Emit(OpCodes.Ldsfld, Decimal_MaxValue); 1147 return; 1148 } 1149 } 1150 else if (value == decimal.MinValue) 1151 { 1152 il.Emit(OpCodes.Ldsfld, Decimal_MinValue); 1153 return; 1154 } 1155 } 1156 1157 il.EmitPrimitive(bits[0]); 1158 il.EmitPrimitive(bits[1]); 1159 il.EmitPrimitive(bits[2]); 1160 il.EmitPrimitive((bits[3] & 0x80000000) != 0); 1161 il.EmitPrimitive(unchecked((byte)scale)); 1162 il.EmitNew(Decimal_Ctor_Int32_Int32_Int32_Bool_Byte); 1163 } 1164 1165 /// <summary> 1166 /// Emits default(T) 1167 /// Semantics match C# compiler behavior 1168 /// </summary> EmitDefault(this ILGenerator il, Type type, ILocalCache locals)1169 internal static void EmitDefault(this ILGenerator il, Type type, ILocalCache locals) 1170 { 1171 switch (type.GetTypeCode()) 1172 { 1173 case TypeCode.DateTime: 1174 il.Emit(OpCodes.Ldsfld, DateTime_MinValue); 1175 break; 1176 1177 case TypeCode.Object: 1178 if (type.IsValueType) 1179 { 1180 // Type.GetTypeCode on an enum returns the underlying 1181 // integer TypeCode, so we won't get here. 1182 Debug.Assert(!type.IsEnum); 1183 1184 // This is the IL for default(T) if T is a generic type 1185 // parameter, so it should work for any type. It's also 1186 // the standard pattern for structs. 1187 LocalBuilder lb = locals.GetLocal(type); 1188 il.Emit(OpCodes.Ldloca, lb); 1189 il.Emit(OpCodes.Initobj, type); 1190 il.Emit(OpCodes.Ldloc, lb); 1191 locals.FreeLocal(lb); 1192 break; 1193 } 1194 1195 goto case TypeCode.Empty; 1196 1197 case TypeCode.Empty: 1198 case TypeCode.String: 1199 case TypeCode.DBNull: 1200 il.Emit(OpCodes.Ldnull); 1201 break; 1202 1203 case TypeCode.Boolean: 1204 case TypeCode.Char: 1205 case TypeCode.SByte: 1206 case TypeCode.Byte: 1207 case TypeCode.Int16: 1208 case TypeCode.UInt16: 1209 case TypeCode.Int32: 1210 case TypeCode.UInt32: 1211 il.Emit(OpCodes.Ldc_I4_0); 1212 break; 1213 1214 case TypeCode.Int64: 1215 case TypeCode.UInt64: 1216 il.Emit(OpCodes.Ldc_I4_0); 1217 il.Emit(OpCodes.Conv_I8); 1218 break; 1219 1220 case TypeCode.Single: 1221 il.Emit(OpCodes.Ldc_R4, default(float)); 1222 break; 1223 1224 case TypeCode.Double: 1225 il.Emit(OpCodes.Ldc_R8, default(double)); 1226 break; 1227 1228 case TypeCode.Decimal: 1229 il.Emit(OpCodes.Ldsfld, Decimal_Zero); 1230 break; 1231 1232 default: 1233 throw ContractUtils.Unreachable; 1234 } 1235 } 1236 1237 #endregion 1238 } 1239 } 1240