1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Microsoft Public License. A
6 * copy of the license can be found in the License.html file at the root of this distribution. If
7 * you cannot locate the Microsoft Public License, please send an email to
8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
9 * by the terms of the Microsoft Public License.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
17 using System.Collections.Generic;
18 using System.Diagnostics;
19 using System.Dynamic.Utils;
20 using System.Reflection;
21 using System.Reflection.Emit;
22 using System.Runtime.CompilerServices;
29 namespace Microsoft.Scripting.Ast.Compiler {
31 namespace System.Linq.Expressions.Compiler {
33 #if CLR2 || SILVERLIGHT
34 using ILGenerator = OffsetTrackingILGenerator;
37 internal static class ILGen {
39 internal static void Emit(this ILGenerator il, OpCode opcode, MethodBase methodBase) {
40 Debug.Assert(methodBase is MethodInfo || methodBase is ConstructorInfo);
42 if (methodBase.MemberType == MemberTypes.Constructor) {
43 il.Emit(opcode, (ConstructorInfo)methodBase);
45 il.Emit(opcode, (MethodInfo)methodBase);
49 #region Instruction helpers
51 internal static void EmitLoadArg(this ILGenerator il, int index) {
52 Debug.Assert(index >= 0);
56 il.Emit(OpCodes.Ldarg_0);
59 il.Emit(OpCodes.Ldarg_1);
62 il.Emit(OpCodes.Ldarg_2);
65 il.Emit(OpCodes.Ldarg_3);
68 if (index <= Byte.MaxValue) {
69 il.Emit(OpCodes.Ldarg_S, (byte)index);
71 il.Emit(OpCodes.Ldarg, index);
77 internal static void EmitLoadArgAddress(this ILGenerator il, int index) {
78 Debug.Assert(index >= 0);
80 if (index <= Byte.MaxValue) {
81 il.Emit(OpCodes.Ldarga_S, (byte)index);
83 il.Emit(OpCodes.Ldarga, index);
87 internal static void EmitStoreArg(this ILGenerator il, int index) {
88 Debug.Assert(index >= 0);
90 if (index <= Byte.MaxValue) {
91 il.Emit(OpCodes.Starg_S, (byte)index);
93 il.Emit(OpCodes.Starg, index);
98 /// Emits a Ldind* instruction for the appropriate type
100 internal static void EmitLoadValueIndirect(this ILGenerator il, Type type) {
101 ContractUtils.RequiresNotNull(type, "type");
103 if (type.IsValueType) {
104 if (type == typeof(int)) {
105 il.Emit(OpCodes.Ldind_I4);
106 } else if (type == typeof(uint)) {
107 il.Emit(OpCodes.Ldind_U4);
108 } else if (type == typeof(short)) {
109 il.Emit(OpCodes.Ldind_I2);
110 } else if (type == typeof(ushort)) {
111 il.Emit(OpCodes.Ldind_U2);
112 } else if (type == typeof(long) || type == typeof(ulong)) {
113 il.Emit(OpCodes.Ldind_I8);
114 } else if (type == typeof(char)) {
115 il.Emit(OpCodes.Ldind_I2);
116 } else if (type == typeof(bool)) {
117 il.Emit(OpCodes.Ldind_I1);
118 } else if (type == typeof(float)) {
119 il.Emit(OpCodes.Ldind_R4);
120 } else if (type == typeof(double)) {
121 il.Emit(OpCodes.Ldind_R8);
123 il.Emit(OpCodes.Ldobj, type);
126 il.Emit(OpCodes.Ldind_Ref);
132 /// Emits a Stind* instruction for the appropriate type.
134 internal static void EmitStoreValueIndirect(this ILGenerator il, Type type) {
135 ContractUtils.RequiresNotNull(type, "type");
137 if (type.IsValueType) {
138 if (type == typeof(int)) {
139 il.Emit(OpCodes.Stind_I4);
140 } else if (type == typeof(short)) {
141 il.Emit(OpCodes.Stind_I2);
142 } else if (type == typeof(long) || type == typeof(ulong)) {
143 il.Emit(OpCodes.Stind_I8);
144 } else if (type == typeof(char)) {
145 il.Emit(OpCodes.Stind_I2);
146 } else if (type == typeof(bool)) {
147 il.Emit(OpCodes.Stind_I1);
148 } else if (type == typeof(float)) {
149 il.Emit(OpCodes.Stind_R4);
150 } else if (type == typeof(double)) {
151 il.Emit(OpCodes.Stind_R8);
153 il.Emit(OpCodes.Stobj, type);
156 il.Emit(OpCodes.Stind_Ref);
160 // Emits the Ldelem* instruction for the appropriate type
162 internal static void EmitLoadElement(this ILGenerator il, Type type) {
163 ContractUtils.RequiresNotNull(type, "type");
165 if (!type.IsValueType) {
166 il.Emit(OpCodes.Ldelem_Ref);
167 } else if (type.IsEnum) {
168 il.Emit(OpCodes.Ldelem, type);
170 switch (Type.GetTypeCode(type)) {
171 case TypeCode.Boolean:
173 il.Emit(OpCodes.Ldelem_I1);
176 il.Emit(OpCodes.Ldelem_U1);
179 il.Emit(OpCodes.Ldelem_I2);
182 case TypeCode.UInt16:
183 il.Emit(OpCodes.Ldelem_U2);
186 il.Emit(OpCodes.Ldelem_I4);
188 case TypeCode.UInt32:
189 il.Emit(OpCodes.Ldelem_U4);
192 case TypeCode.UInt64:
193 il.Emit(OpCodes.Ldelem_I8);
195 case TypeCode.Single:
196 il.Emit(OpCodes.Ldelem_R4);
198 case TypeCode.Double:
199 il.Emit(OpCodes.Ldelem_R8);
202 il.Emit(OpCodes.Ldelem, type);
209 /// Emits a Stelem* instruction for the appropriate type.
211 internal static void EmitStoreElement(this ILGenerator il, Type type) {
212 ContractUtils.RequiresNotNull(type, "type");
215 il.Emit(OpCodes.Stelem, type);
218 switch (Type.GetTypeCode(type)) {
219 case TypeCode.Boolean:
222 il.Emit(OpCodes.Stelem_I1);
226 case TypeCode.UInt16:
227 il.Emit(OpCodes.Stelem_I2);
230 case TypeCode.UInt32:
231 il.Emit(OpCodes.Stelem_I4);
234 case TypeCode.UInt64:
235 il.Emit(OpCodes.Stelem_I8);
237 case TypeCode.Single:
238 il.Emit(OpCodes.Stelem_R4);
240 case TypeCode.Double:
241 il.Emit(OpCodes.Stelem_R8);
244 if (type.IsValueType) {
245 il.Emit(OpCodes.Stelem, type);
247 il.Emit(OpCodes.Stelem_Ref);
253 internal static void EmitType(this ILGenerator il, Type type) {
254 ContractUtils.RequiresNotNull(type, "type");
256 il.Emit(OpCodes.Ldtoken, type);
257 il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
262 #region Fields, properties and methods
264 internal static void EmitFieldAddress(this ILGenerator il, FieldInfo fi) {
265 ContractUtils.RequiresNotNull(fi, "fi");
268 il.Emit(OpCodes.Ldsflda, fi);
270 il.Emit(OpCodes.Ldflda, fi);
274 internal static void EmitFieldGet(this ILGenerator il, FieldInfo fi) {
275 ContractUtils.RequiresNotNull(fi, "fi");
278 il.Emit(OpCodes.Ldsfld, fi);
280 il.Emit(OpCodes.Ldfld, fi);
284 internal static void EmitFieldSet(this ILGenerator il, FieldInfo fi) {
285 ContractUtils.RequiresNotNull(fi, "fi");
288 il.Emit(OpCodes.Stsfld, fi);
290 il.Emit(OpCodes.Stfld, fi);
294 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
295 internal static void EmitNew(this ILGenerator il, ConstructorInfo ci) {
296 ContractUtils.RequiresNotNull(ci, "ci");
298 if (ci.DeclaringType.ContainsGenericParameters) {
299 throw Error.IllegalNewGenericParams(ci.DeclaringType);
302 il.Emit(OpCodes.Newobj, ci);
305 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
306 internal static void EmitNew(this ILGenerator il, Type type, Type[] paramTypes) {
307 ContractUtils.RequiresNotNull(type, "type");
308 ContractUtils.RequiresNotNull(paramTypes, "paramTypes");
310 ConstructorInfo ci = type.GetConstructor(paramTypes);
311 if (ci == null) throw Error.TypeDoesNotHaveConstructorForTheSignature();
319 internal static void EmitNull(this ILGenerator il) {
320 il.Emit(OpCodes.Ldnull);
323 internal static void EmitString(this ILGenerator il, string value) {
324 ContractUtils.RequiresNotNull(value, "value");
325 il.Emit(OpCodes.Ldstr, value);
328 internal static void EmitBoolean(this ILGenerator il, bool value) {
330 il.Emit(OpCodes.Ldc_I4_1);
332 il.Emit(OpCodes.Ldc_I4_0);
336 internal static void EmitChar(this ILGenerator il, char value) {
338 il.Emit(OpCodes.Conv_U2);
341 internal static void EmitByte(this ILGenerator il, byte value) {
343 il.Emit(OpCodes.Conv_U1);
346 internal static void EmitSByte(this ILGenerator il, sbyte value) {
348 il.Emit(OpCodes.Conv_I1);
351 internal static void EmitShort(this ILGenerator il, short value) {
353 il.Emit(OpCodes.Conv_I2);
356 internal static void EmitUShort(this ILGenerator il, ushort value) {
358 il.Emit(OpCodes.Conv_U2);
361 internal static void EmitInt(this ILGenerator il, int value) {
365 c = OpCodes.Ldc_I4_M1;
368 c = OpCodes.Ldc_I4_0;
371 c = OpCodes.Ldc_I4_1;
374 c = OpCodes.Ldc_I4_2;
377 c = OpCodes.Ldc_I4_3;
380 c = OpCodes.Ldc_I4_4;
383 c = OpCodes.Ldc_I4_5;
386 c = OpCodes.Ldc_I4_6;
389 c = OpCodes.Ldc_I4_7;
392 c = OpCodes.Ldc_I4_8;
395 if (value >= -128 && value <= 127) {
396 il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
398 il.Emit(OpCodes.Ldc_I4, value);
405 internal static void EmitUInt(this ILGenerator il, uint value) {
406 il.EmitInt((int)value);
407 il.Emit(OpCodes.Conv_U4);
410 internal static void EmitLong(this ILGenerator il, long value) {
411 il.Emit(OpCodes.Ldc_I8, value);
414 // Now, emit convert to give the constant type information.
416 // Otherwise, it is treated as unsigned and overflow is not
417 // detected if it's used in checked ops.
419 il.Emit(OpCodes.Conv_I8);
422 internal static void EmitULong(this ILGenerator il, ulong value) {
423 il.Emit(OpCodes.Ldc_I8, (long)value);
424 il.Emit(OpCodes.Conv_U8);
427 internal static void EmitDouble(this ILGenerator il, double value) {
428 il.Emit(OpCodes.Ldc_R8, value);
431 internal static void EmitSingle(this ILGenerator il, float value) {
432 il.Emit(OpCodes.Ldc_R4, value);
435 // matches TryEmitConstant
436 internal static bool CanEmitConstant(object value, Type type) {
437 if (value == null || CanEmitILConstant(type)) {
441 Type t = value as Type;
442 if (t != null && ShouldLdtoken(t)) {
446 MethodBase mb = value as MethodBase;
447 if (mb != null && ShouldLdtoken(mb)) {
454 // matches TryEmitILConstant
455 private static bool CanEmitILConstant(Type type) {
456 switch (Type.GetTypeCode(type)) {
457 case TypeCode.Boolean:
462 case TypeCode.Single:
463 case TypeCode.Double:
466 case TypeCode.UInt16:
467 case TypeCode.UInt32:
468 case TypeCode.UInt64:
469 case TypeCode.Decimal:
470 case TypeCode.String:
476 internal static void EmitConstant(this ILGenerator il, object value) {
477 Debug.Assert(value != null);
478 EmitConstant(il, value, value.GetType());
483 // Note: we support emitting more things as IL constants than
485 internal static void EmitConstant(this ILGenerator il, object value, Type type) {
487 // Smarter than the Linq implementation which uses the initobj
488 // pattern for all value types (works, but requires a local and
490 il.EmitDefault(type);
494 // Handle the easy cases
495 if (il.TryEmitILConstant(value, type)) {
499 // Check for a few more types that we support emitting as constants
500 Type t = value as Type;
501 if (t != null && ShouldLdtoken(t)) {
503 if (type != typeof(Type)) {
504 il.Emit(OpCodes.Castclass, type);
509 MethodBase mb = value as MethodBase;
510 if (mb != null && ShouldLdtoken(mb)) {
511 il.Emit(OpCodes.Ldtoken, mb);
512 Type dt = mb.DeclaringType;
513 if (dt != null && dt.IsGenericType) {
514 il.Emit(OpCodes.Ldtoken, dt);
515 il.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetMethodFromHandle", new Type[] { typeof(RuntimeMethodHandle), typeof(RuntimeTypeHandle) }));
517 il.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetMethodFromHandle", new Type[] { typeof(RuntimeMethodHandle) }));
519 if (type != typeof(MethodBase)) {
520 il.Emit(OpCodes.Castclass, type);
525 throw ContractUtils.Unreachable;
528 internal static bool ShouldLdtoken(Type t) {
529 return t is TypeBuilder || t.IsGenericParameter || t.IsVisible;
532 internal static bool ShouldLdtoken(MethodBase mb) {
533 // Can't ldtoken on a DynamicMethod
534 if (mb is DynamicMethod) {
538 Type dt = mb.DeclaringType;
539 return dt == null || ShouldLdtoken(dt);
543 private static bool TryEmitILConstant(this ILGenerator il, object value, Type type) {
544 switch (Type.GetTypeCode(type)) {
545 case TypeCode.Boolean:
546 il.EmitBoolean((bool)value);
549 il.EmitSByte((sbyte)value);
552 il.EmitShort((short)value);
555 il.EmitInt((int)value);
558 il.EmitLong((long)value);
560 case TypeCode.Single:
561 il.EmitSingle((float)value);
563 case TypeCode.Double:
564 il.EmitDouble((double)value);
567 il.EmitChar((char)value);
570 il.EmitByte((byte)value);
572 case TypeCode.UInt16:
573 il.EmitUShort((ushort)value);
575 case TypeCode.UInt32:
576 il.EmitUInt((uint)value);
578 case TypeCode.UInt64:
579 il.EmitULong((ulong)value);
581 case TypeCode.Decimal:
582 il.EmitDecimal((decimal)value);
584 case TypeCode.String:
585 il.EmitString((string)value);
594 #region Linq Conversions
596 internal static void EmitConvertToType(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
597 if (TypeUtils.AreEquivalent(typeFrom, typeTo)) {
601 if (typeFrom == typeof(void) || typeTo == typeof(void)) {
602 throw ContractUtils.Unreachable;
605 bool isTypeFromNullable = TypeUtils.IsNullableType(typeFrom);
606 bool isTypeToNullable = TypeUtils.IsNullableType(typeTo);
608 Type nnExprType = TypeUtils.GetNonNullableType(typeFrom);
609 Type nnType = TypeUtils.GetNonNullableType(typeTo);
611 if (typeFrom.IsInterface || // interface cast
612 typeTo.IsInterface ||
613 typeFrom == typeof(object) || // boxing cast
614 typeTo == typeof(object)) {
615 il.EmitCastToType(typeFrom, typeTo);
616 } else if (isTypeFromNullable || isTypeToNullable) {
617 il.EmitNullableConversion(typeFrom, typeTo, isChecked);
618 } else if (!(TypeUtils.IsConvertible(typeFrom) && TypeUtils.IsConvertible(typeTo)) // primitive runtime conversion
620 (nnExprType.IsAssignableFrom(nnType) || // down cast
621 nnType.IsAssignableFrom(nnExprType))) // up cast
623 il.EmitCastToType(typeFrom, typeTo);
624 } else if (typeFrom.IsArray && typeTo.IsArray) {
625 // See DevDiv Bugs #94657.
626 il.EmitCastToType(typeFrom, typeTo);
628 il.EmitNumericConversion(typeFrom, typeTo, isChecked);
633 private static void EmitCastToType(this ILGenerator il, Type typeFrom, Type typeTo) {
634 if (!typeFrom.IsValueType && typeTo.IsValueType) {
635 il.Emit(OpCodes.Unbox_Any, typeTo);
636 } else if (typeFrom.IsValueType && !typeTo.IsValueType) {
637 il.Emit(OpCodes.Box, typeFrom);
638 if (typeTo != typeof(object)) {
639 il.Emit(OpCodes.Castclass, typeTo);
641 } else if (!typeFrom.IsValueType && !typeTo.IsValueType) {
642 il.Emit(OpCodes.Castclass, typeTo);
644 throw Error.InvalidCast(typeFrom, typeTo);
649 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
650 private static void EmitNumericConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
651 bool isFromUnsigned = TypeUtils.IsUnsigned(typeFrom);
652 bool isFromFloatingPoint = TypeUtils.IsFloatingPoint(typeFrom);
653 if (typeTo == typeof(Single)) {
655 il.Emit(OpCodes.Conv_R_Un);
656 il.Emit(OpCodes.Conv_R4);
657 } else if (typeTo == typeof(Double)) {
659 il.Emit(OpCodes.Conv_R_Un);
660 il.Emit(OpCodes.Conv_R8);
662 TypeCode tc = Type.GetTypeCode(typeTo);
664 if (isFromUnsigned) {
667 il.Emit(OpCodes.Conv_Ovf_I1_Un);
670 il.Emit(OpCodes.Conv_Ovf_I2_Un);
673 il.Emit(OpCodes.Conv_Ovf_I4_Un);
676 il.Emit(OpCodes.Conv_Ovf_I8_Un);
679 il.Emit(OpCodes.Conv_Ovf_U1_Un);
681 case TypeCode.UInt16:
683 il.Emit(OpCodes.Conv_Ovf_U2_Un);
685 case TypeCode.UInt32:
686 il.Emit(OpCodes.Conv_Ovf_U4_Un);
688 case TypeCode.UInt64:
689 il.Emit(OpCodes.Conv_Ovf_U8_Un);
692 throw Error.UnhandledConvert(typeTo);
697 il.Emit(OpCodes.Conv_Ovf_I1);
700 il.Emit(OpCodes.Conv_Ovf_I2);
703 il.Emit(OpCodes.Conv_Ovf_I4);
706 il.Emit(OpCodes.Conv_Ovf_I8);
709 il.Emit(OpCodes.Conv_Ovf_U1);
711 case TypeCode.UInt16:
713 il.Emit(OpCodes.Conv_Ovf_U2);
715 case TypeCode.UInt32:
716 il.Emit(OpCodes.Conv_Ovf_U4);
718 case TypeCode.UInt64:
719 il.Emit(OpCodes.Conv_Ovf_U8);
722 throw Error.UnhandledConvert(typeTo);
726 if (isFromUnsigned) {
730 il.Emit(OpCodes.Conv_U1);
733 case TypeCode.UInt16:
735 il.Emit(OpCodes.Conv_U2);
738 case TypeCode.UInt32:
739 il.Emit(OpCodes.Conv_U4);
742 case TypeCode.UInt64:
743 il.Emit(OpCodes.Conv_U8);
746 throw Error.UnhandledConvert(typeTo);
752 il.Emit(OpCodes.Conv_I1);
755 case TypeCode.UInt16:
757 il.Emit(OpCodes.Conv_I2);
760 case TypeCode.UInt32:
761 il.Emit(OpCodes.Conv_I4);
764 case TypeCode.UInt64:
765 il.Emit(OpCodes.Conv_I8);
768 throw Error.UnhandledConvert(typeTo);
776 private static void EmitNullableToNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
777 Debug.Assert(TypeUtils.IsNullableType(typeFrom));
778 Debug.Assert(TypeUtils.IsNullableType(typeTo));
779 Label labIfNull = default(Label);
780 Label labEnd = default(Label);
781 LocalBuilder locFrom = null;
782 LocalBuilder locTo = null;
783 locFrom = il.DeclareLocal(typeFrom);
784 il.Emit(OpCodes.Stloc, locFrom);
785 locTo = il.DeclareLocal(typeTo);
787 il.Emit(OpCodes.Ldloca, locFrom);
788 il.EmitHasValue(typeFrom);
789 labIfNull = il.DefineLabel();
790 il.Emit(OpCodes.Brfalse_S, labIfNull);
791 il.Emit(OpCodes.Ldloca, locFrom);
792 il.EmitGetValueOrDefault(typeFrom);
793 Type nnTypeFrom = TypeUtils.GetNonNullableType(typeFrom);
794 Type nnTypeTo = TypeUtils.GetNonNullableType(typeTo);
795 il.EmitConvertToType(nnTypeFrom, nnTypeTo, isChecked);
796 // construct result type
797 ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo });
798 il.Emit(OpCodes.Newobj, ci);
799 il.Emit(OpCodes.Stloc, locTo);
800 labEnd = il.DefineLabel();
801 il.Emit(OpCodes.Br_S, labEnd);
802 // if null then create a default one
803 il.MarkLabel(labIfNull);
804 il.Emit(OpCodes.Ldloca, locTo);
805 il.Emit(OpCodes.Initobj, typeTo);
806 il.MarkLabel(labEnd);
807 il.Emit(OpCodes.Ldloc, locTo);
811 private static void EmitNonNullableToNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
812 Debug.Assert(!TypeUtils.IsNullableType(typeFrom));
813 Debug.Assert(TypeUtils.IsNullableType(typeTo));
814 LocalBuilder locTo = null;
815 locTo = il.DeclareLocal(typeTo);
816 Type nnTypeTo = TypeUtils.GetNonNullableType(typeTo);
817 il.EmitConvertToType(typeFrom, nnTypeTo, isChecked);
818 ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo });
819 il.Emit(OpCodes.Newobj, ci);
820 il.Emit(OpCodes.Stloc, locTo);
821 il.Emit(OpCodes.Ldloc, locTo);
825 private static void EmitNullableToNonNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
826 Debug.Assert(TypeUtils.IsNullableType(typeFrom));
827 Debug.Assert(!TypeUtils.IsNullableType(typeTo));
828 if (typeTo.IsValueType)
829 il.EmitNullableToNonNullableStructConversion(typeFrom, typeTo, isChecked);
831 il.EmitNullableToReferenceConversion(typeFrom);
835 private static void EmitNullableToNonNullableStructConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
836 Debug.Assert(TypeUtils.IsNullableType(typeFrom));
837 Debug.Assert(!TypeUtils.IsNullableType(typeTo));
838 Debug.Assert(typeTo.IsValueType);
839 LocalBuilder locFrom = null;
840 locFrom = il.DeclareLocal(typeFrom);
841 il.Emit(OpCodes.Stloc, locFrom);
842 il.Emit(OpCodes.Ldloca, locFrom);
843 il.EmitGetValue(typeFrom);
844 Type nnTypeFrom = TypeUtils.GetNonNullableType(typeFrom);
845 il.EmitConvertToType(nnTypeFrom, typeTo, isChecked);
849 private static void EmitNullableToReferenceConversion(this ILGenerator il, Type typeFrom) {
850 Debug.Assert(TypeUtils.IsNullableType(typeFrom));
851 // We've got a conversion from nullable to Object, ValueType, Enum, etc. Just box it so that
852 // we get the nullable semantics.
853 il.Emit(OpCodes.Box, typeFrom);
857 private static void EmitNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
858 bool isTypeFromNullable = TypeUtils.IsNullableType(typeFrom);
859 bool isTypeToNullable = TypeUtils.IsNullableType(typeTo);
860 Debug.Assert(isTypeFromNullable || isTypeToNullable);
861 if (isTypeFromNullable && isTypeToNullable)
862 il.EmitNullableToNullableConversion(typeFrom, typeTo, isChecked);
863 else if (isTypeFromNullable)
864 il.EmitNullableToNonNullableConversion(typeFrom, typeTo, isChecked);
866 il.EmitNonNullableToNullableConversion(typeFrom, typeTo, isChecked);
870 internal static void EmitHasValue(this ILGenerator il, Type nullableType) {
871 MethodInfo mi = nullableType.GetMethod("get_HasValue", BindingFlags.Instance | BindingFlags.Public);
872 Debug.Assert(nullableType.IsValueType);
873 il.Emit(OpCodes.Call, mi);
877 internal static void EmitGetValue(this ILGenerator il, Type nullableType) {
878 MethodInfo mi = nullableType.GetMethod("get_Value", BindingFlags.Instance | BindingFlags.Public);
879 Debug.Assert(nullableType.IsValueType);
880 il.Emit(OpCodes.Call, mi);
884 internal static void EmitGetValueOrDefault(this ILGenerator il, Type nullableType) {
885 MethodInfo mi = nullableType.GetMethod("GetValueOrDefault", System.Type.EmptyTypes);
886 Debug.Assert(nullableType.IsValueType);
887 il.Emit(OpCodes.Call, mi);
895 /// Emits an array of constant values provided in the given list.
896 /// The array is strongly typed.
898 internal static void EmitArray<T>(this ILGenerator il, IList<T> items) {
899 ContractUtils.RequiresNotNull(items, "items");
901 il.EmitInt(items.Count);
902 il.Emit(OpCodes.Newarr, typeof(T));
903 for (int i = 0; i < items.Count; i++) {
904 il.Emit(OpCodes.Dup);
906 il.EmitConstant(items[i], typeof(T));
907 il.EmitStoreElement(typeof(T));
912 /// Emits an array of values of count size. The items are emitted via the callback
913 /// which is provided with the current item index to emit.
915 internal static void EmitArray(this ILGenerator il, Type elementType, int count, Action<int> emit) {
916 ContractUtils.RequiresNotNull(elementType, "elementType");
917 ContractUtils.RequiresNotNull(emit, "emit");
918 if (count < 0) throw Error.CountCannotBeNegative();
921 il.Emit(OpCodes.Newarr, elementType);
922 for (int i = 0; i < count; i++) {
923 il.Emit(OpCodes.Dup);
928 il.EmitStoreElement(elementType);
933 /// Emits an array construction code.
934 /// The code assumes that bounds for all dimensions
935 /// are already emitted.
937 internal static void EmitArray(this ILGenerator il, Type arrayType) {
938 ContractUtils.RequiresNotNull(arrayType, "arrayType");
939 if (!arrayType.IsArray) throw Error.ArrayTypeMustBeArray();
941 int rank = arrayType.GetArrayRank();
943 il.Emit(OpCodes.Newarr, arrayType.GetElementType());
945 Type[] types = new Type[rank];
946 for (int i = 0; i < rank; i++) {
947 types[i] = typeof(int);
949 il.EmitNew(arrayType, types);
955 #region Support for emitting constants
957 internal static void EmitDecimal(this ILGenerator il, decimal value) {
958 if (Decimal.Truncate(value) == value) {
959 if (Int32.MinValue <= value && value <= Int32.MaxValue) {
960 int intValue = Decimal.ToInt32(value);
961 il.EmitInt(intValue);
962 il.EmitNew(typeof(Decimal).GetConstructor(new Type[] { typeof(int) }));
963 } else if (Int64.MinValue <= value && value <= Int64.MaxValue) {
964 long longValue = Decimal.ToInt64(value);
965 il.EmitLong(longValue);
966 il.EmitNew(typeof(Decimal).GetConstructor(new Type[] { typeof(long) }));
968 il.EmitDecimalBits(value);
971 il.EmitDecimalBits(value);
975 private static void EmitDecimalBits(this ILGenerator il, decimal value) {
976 int[] bits = Decimal.GetBits(value);
980 il.EmitBoolean((bits[3] & 0x80000000) != 0);
981 il.EmitByte((byte)(bits[3] >> 16));
982 il.EmitNew(typeof(decimal).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int), typeof(bool), typeof(byte) }));
987 /// Semantics match C# compiler behavior
989 internal static void EmitDefault(this ILGenerator il, Type type) {
990 switch (Type.GetTypeCode(type)) {
991 case TypeCode.Object:
992 case TypeCode.DateTime:
993 if (type.IsValueType) {
994 // Type.GetTypeCode on an enum returns the underlying
995 // integer TypeCode, so we won't get here.
996 Debug.Assert(!type.IsEnum);
998 // This is the IL for default(T) if T is a generic type
999 // parameter, so it should work for any type. It's also
1000 // the standard pattern for structs.
1001 LocalBuilder lb = il.DeclareLocal(type);
1002 il.Emit(OpCodes.Ldloca, lb);
1003 il.Emit(OpCodes.Initobj, type);
1004 il.Emit(OpCodes.Ldloc, lb);
1006 il.Emit(OpCodes.Ldnull);
1010 case TypeCode.Empty:
1011 case TypeCode.String:
1012 case TypeCode.DBNull:
1013 il.Emit(OpCodes.Ldnull);
1016 case TypeCode.Boolean:
1018 case TypeCode.SByte:
1020 case TypeCode.Int16:
1021 case TypeCode.UInt16:
1022 case TypeCode.Int32:
1023 case TypeCode.UInt32:
1024 il.Emit(OpCodes.Ldc_I4_0);
1027 case TypeCode.Int64:
1028 case TypeCode.UInt64:
1029 il.Emit(OpCodes.Ldc_I4_0);
1030 il.Emit(OpCodes.Conv_I8);
1033 case TypeCode.Single:
1034 il.Emit(OpCodes.Ldc_R4, default(Single));
1037 case TypeCode.Double:
1038 il.Emit(OpCodes.Ldc_R8, default(Double));
1041 case TypeCode.Decimal:
1042 il.Emit(OpCodes.Ldc_I4_0);
1043 il.Emit(OpCodes.Newobj, typeof(Decimal).GetConstructor(new Type[] { typeof(int) }));
1047 throw ContractUtils.Unreachable;