1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. 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 Apache License, Version 2.0, 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 Apache License, Version 2.0.
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;
23 using Microsoft.Scripting.Utils;
26 namespace Microsoft.Scripting.Ast.Compiler {
28 namespace System.Linq.Expressions.Compiler {
30 #if !FEATURE_CORE_DLR || SILVERLIGHT
31 using ILGenerator = OffsetTrackingILGenerator;
34 internal static class ILGen {
36 internal static void Emit(this ILGenerator il, OpCode opcode, MethodBase methodBase) {
37 Debug.Assert(methodBase is MethodInfo || methodBase is ConstructorInfo);
39 if (methodBase.MemberType == MemberTypes.Constructor) {
40 il.Emit(opcode, (ConstructorInfo)methodBase);
42 il.Emit(opcode, (MethodInfo)methodBase);
46 #region Instruction helpers
48 internal static void EmitLoadArg(this ILGenerator il, int index) {
49 Debug.Assert(index >= 0);
53 il.Emit(OpCodes.Ldarg_0);
56 il.Emit(OpCodes.Ldarg_1);
59 il.Emit(OpCodes.Ldarg_2);
62 il.Emit(OpCodes.Ldarg_3);
65 if (index <= Byte.MaxValue) {
66 il.Emit(OpCodes.Ldarg_S, (byte)index);
68 il.Emit(OpCodes.Ldarg, index);
74 internal static void EmitLoadArgAddress(this ILGenerator il, int index) {
75 Debug.Assert(index >= 0);
77 if (index <= Byte.MaxValue) {
78 il.Emit(OpCodes.Ldarga_S, (byte)index);
80 il.Emit(OpCodes.Ldarga, index);
84 internal static void EmitStoreArg(this ILGenerator il, int index) {
85 Debug.Assert(index >= 0);
87 if (index <= Byte.MaxValue) {
88 il.Emit(OpCodes.Starg_S, (byte)index);
90 il.Emit(OpCodes.Starg, index);
95 /// Emits a Ldind* instruction for the appropriate type
97 internal static void EmitLoadValueIndirect(this ILGenerator il, Type type) {
98 ContractUtils.RequiresNotNull(type, "type");
100 if (type.IsValueType) {
101 if (type == typeof(int)) {
102 il.Emit(OpCodes.Ldind_I4);
103 } else if (type == typeof(uint)) {
104 il.Emit(OpCodes.Ldind_U4);
105 } else if (type == typeof(short)) {
106 il.Emit(OpCodes.Ldind_I2);
107 } else if (type == typeof(ushort)) {
108 il.Emit(OpCodes.Ldind_U2);
109 } else if (type == typeof(long) || type == typeof(ulong)) {
110 il.Emit(OpCodes.Ldind_I8);
111 } else if (type == typeof(char)) {
112 il.Emit(OpCodes.Ldind_I2);
113 } else if (type == typeof(bool)) {
114 il.Emit(OpCodes.Ldind_I1);
115 } else if (type == typeof(float)) {
116 il.Emit(OpCodes.Ldind_R4);
117 } else if (type == typeof(double)) {
118 il.Emit(OpCodes.Ldind_R8);
120 il.Emit(OpCodes.Ldobj, type);
123 il.Emit(OpCodes.Ldind_Ref);
129 /// Emits a Stind* instruction for the appropriate type.
131 internal static void EmitStoreValueIndirect(this ILGenerator il, Type type) {
132 ContractUtils.RequiresNotNull(type, "type");
134 if (type.IsValueType) {
135 if (type == typeof(int)) {
136 il.Emit(OpCodes.Stind_I4);
137 } else if (type == typeof(short)) {
138 il.Emit(OpCodes.Stind_I2);
139 } else if (type == typeof(long) || type == typeof(ulong)) {
140 il.Emit(OpCodes.Stind_I8);
141 } else if (type == typeof(char)) {
142 il.Emit(OpCodes.Stind_I2);
143 } else if (type == typeof(bool)) {
144 il.Emit(OpCodes.Stind_I1);
145 } else if (type == typeof(float)) {
146 il.Emit(OpCodes.Stind_R4);
147 } else if (type == typeof(double)) {
148 il.Emit(OpCodes.Stind_R8);
150 il.Emit(OpCodes.Stobj, type);
153 il.Emit(OpCodes.Stind_Ref);
157 // Emits the Ldelem* instruction for the appropriate type
159 internal static void EmitLoadElement(this ILGenerator il, Type type) {
160 ContractUtils.RequiresNotNull(type, "type");
162 if (!type.IsValueType) {
163 il.Emit(OpCodes.Ldelem_Ref);
164 } else if (type.IsEnum) {
165 il.Emit(OpCodes.Ldelem, type);
167 switch (Type.GetTypeCode(type)) {
168 case TypeCode.Boolean:
170 il.Emit(OpCodes.Ldelem_I1);
173 il.Emit(OpCodes.Ldelem_U1);
176 il.Emit(OpCodes.Ldelem_I2);
179 case TypeCode.UInt16:
180 il.Emit(OpCodes.Ldelem_U2);
183 il.Emit(OpCodes.Ldelem_I4);
185 case TypeCode.UInt32:
186 il.Emit(OpCodes.Ldelem_U4);
189 case TypeCode.UInt64:
190 il.Emit(OpCodes.Ldelem_I8);
192 case TypeCode.Single:
193 il.Emit(OpCodes.Ldelem_R4);
195 case TypeCode.Double:
196 il.Emit(OpCodes.Ldelem_R8);
199 il.Emit(OpCodes.Ldelem, type);
206 /// Emits a Stelem* instruction for the appropriate type.
208 internal static void EmitStoreElement(this ILGenerator il, Type type) {
209 ContractUtils.RequiresNotNull(type, "type");
212 il.Emit(OpCodes.Stelem, type);
215 switch (Type.GetTypeCode(type)) {
216 case TypeCode.Boolean:
219 il.Emit(OpCodes.Stelem_I1);
223 case TypeCode.UInt16:
224 il.Emit(OpCodes.Stelem_I2);
227 case TypeCode.UInt32:
228 il.Emit(OpCodes.Stelem_I4);
231 case TypeCode.UInt64:
232 il.Emit(OpCodes.Stelem_I8);
234 case TypeCode.Single:
235 il.Emit(OpCodes.Stelem_R4);
237 case TypeCode.Double:
238 il.Emit(OpCodes.Stelem_R8);
241 if (type.IsValueType) {
242 il.Emit(OpCodes.Stelem, type);
244 il.Emit(OpCodes.Stelem_Ref);
250 internal static void EmitType(this ILGenerator il, Type type) {
251 ContractUtils.RequiresNotNull(type, "type");
253 il.Emit(OpCodes.Ldtoken, type);
254 il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
259 #region Fields, properties and methods
261 internal static void EmitFieldAddress(this ILGenerator il, FieldInfo fi) {
262 ContractUtils.RequiresNotNull(fi, "fi");
265 il.Emit(OpCodes.Ldsflda, fi);
267 il.Emit(OpCodes.Ldflda, fi);
271 internal static void EmitFieldGet(this ILGenerator il, FieldInfo fi) {
272 ContractUtils.RequiresNotNull(fi, "fi");
275 il.Emit(OpCodes.Ldsfld, fi);
277 il.Emit(OpCodes.Ldfld, fi);
281 internal static void EmitFieldSet(this ILGenerator il, FieldInfo fi) {
282 ContractUtils.RequiresNotNull(fi, "fi");
285 il.Emit(OpCodes.Stsfld, fi);
287 il.Emit(OpCodes.Stfld, fi);
291 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
292 internal static void EmitNew(this ILGenerator il, ConstructorInfo ci) {
293 ContractUtils.RequiresNotNull(ci, "ci");
295 if (ci.DeclaringType.ContainsGenericParameters) {
296 throw Error.IllegalNewGenericParams(ci.DeclaringType);
299 il.Emit(OpCodes.Newobj, ci);
302 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
303 internal static void EmitNew(this ILGenerator il, Type type, Type[] paramTypes) {
304 ContractUtils.RequiresNotNull(type, "type");
305 ContractUtils.RequiresNotNull(paramTypes, "paramTypes");
307 ConstructorInfo ci = type.GetConstructor(paramTypes);
308 if (ci == null) throw Error.TypeDoesNotHaveConstructorForTheSignature();
316 internal static void EmitNull(this ILGenerator il) {
317 il.Emit(OpCodes.Ldnull);
320 internal static void EmitString(this ILGenerator il, string value) {
321 ContractUtils.RequiresNotNull(value, "value");
322 il.Emit(OpCodes.Ldstr, value);
325 internal static void EmitBoolean(this ILGenerator il, bool value) {
327 il.Emit(OpCodes.Ldc_I4_1);
329 il.Emit(OpCodes.Ldc_I4_0);
333 internal static void EmitChar(this ILGenerator il, char value) {
335 il.Emit(OpCodes.Conv_U2);
338 internal static void EmitByte(this ILGenerator il, byte value) {
340 il.Emit(OpCodes.Conv_U1);
343 internal static void EmitSByte(this ILGenerator il, sbyte value) {
345 il.Emit(OpCodes.Conv_I1);
348 internal static void EmitShort(this ILGenerator il, short value) {
350 il.Emit(OpCodes.Conv_I2);
353 internal static void EmitUShort(this ILGenerator il, ushort value) {
355 il.Emit(OpCodes.Conv_U2);
358 internal static void EmitInt(this ILGenerator il, int value) {
362 c = OpCodes.Ldc_I4_M1;
365 c = OpCodes.Ldc_I4_0;
368 c = OpCodes.Ldc_I4_1;
371 c = OpCodes.Ldc_I4_2;
374 c = OpCodes.Ldc_I4_3;
377 c = OpCodes.Ldc_I4_4;
380 c = OpCodes.Ldc_I4_5;
383 c = OpCodes.Ldc_I4_6;
386 c = OpCodes.Ldc_I4_7;
389 c = OpCodes.Ldc_I4_8;
392 if (value >= -128 && value <= 127) {
393 il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
395 il.Emit(OpCodes.Ldc_I4, value);
402 internal static void EmitUInt(this ILGenerator il, uint value) {
403 il.EmitInt((int)value);
404 il.Emit(OpCodes.Conv_U4);
407 internal static void EmitLong(this ILGenerator il, long value) {
408 il.Emit(OpCodes.Ldc_I8, value);
411 // Now, emit convert to give the constant type information.
413 // Otherwise, it is treated as unsigned and overflow is not
414 // detected if it's used in checked ops.
416 il.Emit(OpCodes.Conv_I8);
419 internal static void EmitULong(this ILGenerator il, ulong value) {
420 il.Emit(OpCodes.Ldc_I8, (long)value);
421 il.Emit(OpCodes.Conv_U8);
424 internal static void EmitDouble(this ILGenerator il, double value) {
425 il.Emit(OpCodes.Ldc_R8, value);
428 internal static void EmitSingle(this ILGenerator il, float value) {
429 il.Emit(OpCodes.Ldc_R4, value);
432 // matches TryEmitConstant
433 internal static bool CanEmitConstant(object value, Type type) {
434 if (value == null || CanEmitILConstant(type)) {
438 Type t = value as Type;
439 if (t != null && ShouldLdtoken(t)) {
443 MethodBase mb = value as MethodBase;
444 if (mb != null && ShouldLdtoken(mb)) {
451 // matches TryEmitILConstant
452 private static bool CanEmitILConstant(Type type) {
453 switch (Type.GetTypeCode(type)) {
454 case TypeCode.Boolean:
459 case TypeCode.Single:
460 case TypeCode.Double:
463 case TypeCode.UInt16:
464 case TypeCode.UInt32:
465 case TypeCode.UInt64:
466 case TypeCode.Decimal:
467 case TypeCode.String:
473 internal static void EmitConstant(this ILGenerator il, object value) {
474 Debug.Assert(value != null);
475 EmitConstant(il, value, value.GetType());
480 // Note: we support emitting more things as IL constants than
482 internal static void EmitConstant(this ILGenerator il, object value, Type type) {
484 // Smarter than the Linq implementation which uses the initobj
485 // pattern for all value types (works, but requires a local and
487 il.EmitDefault(type);
491 // Handle the easy cases
492 if (il.TryEmitILConstant(value, type)) {
496 // Check for a few more types that we support emitting as constants
497 Type t = value as Type;
498 if (t != null && ShouldLdtoken(t)) {
500 if (type != typeof(Type)) {
501 il.Emit(OpCodes.Castclass, type);
506 MethodBase mb = value as MethodBase;
507 if (mb != null && ShouldLdtoken(mb)) {
508 il.Emit(OpCodes.Ldtoken, mb);
509 Type dt = mb.DeclaringType;
510 if (dt != null && dt.IsGenericType) {
511 il.Emit(OpCodes.Ldtoken, dt);
512 il.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetMethodFromHandle", new Type[] { typeof(RuntimeMethodHandle), typeof(RuntimeTypeHandle) }));
514 il.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetMethodFromHandle", new Type[] { typeof(RuntimeMethodHandle) }));
516 if (type != typeof(MethodBase)) {
517 il.Emit(OpCodes.Castclass, type);
522 throw ContractUtils.Unreachable;
525 internal static bool ShouldLdtoken(Type t) {
527 if (t is TypeBuilder) {
531 return t.IsGenericParameter || t.IsVisible;
534 internal static bool ShouldLdtoken(MethodBase mb) {
535 // Can't ldtoken on a DynamicMethod
536 if (mb is DynamicMethod) {
540 Type dt = mb.DeclaringType;
541 return dt == null || ShouldLdtoken(dt);
545 private static bool TryEmitILConstant(this ILGenerator il, object value, Type type) {
546 switch (Type.GetTypeCode(type)) {
547 case TypeCode.Boolean:
548 il.EmitBoolean((bool)value);
551 il.EmitSByte((sbyte)value);
554 il.EmitShort((short)value);
557 il.EmitInt((int)value);
560 il.EmitLong((long)value);
562 case TypeCode.Single:
563 il.EmitSingle((float)value);
565 case TypeCode.Double:
566 il.EmitDouble((double)value);
569 il.EmitChar((char)value);
572 il.EmitByte((byte)value);
574 case TypeCode.UInt16:
575 il.EmitUShort((ushort)value);
577 case TypeCode.UInt32:
578 il.EmitUInt((uint)value);
580 case TypeCode.UInt64:
581 il.EmitULong((ulong)value);
583 case TypeCode.Decimal:
584 il.EmitDecimal((decimal)value);
586 case TypeCode.String:
587 il.EmitString((string)value);
596 #region Linq Conversions
598 internal static void EmitConvertToType(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
599 if (TypeUtils.AreEquivalent(typeFrom, typeTo)) {
603 if (typeFrom == typeof(void) || typeTo == typeof(void)) {
604 throw ContractUtils.Unreachable;
607 bool isTypeFromNullable = TypeUtils.IsNullableType(typeFrom);
608 bool isTypeToNullable = TypeUtils.IsNullableType(typeTo);
610 Type nnExprType = TypeUtils.GetNonNullableType(typeFrom);
611 Type nnType = TypeUtils.GetNonNullableType(typeTo);
613 if (typeFrom.IsInterface || // interface cast
614 typeTo.IsInterface ||
615 typeFrom == typeof(object) || // boxing cast
616 typeTo == typeof(object) ||
617 typeFrom == typeof(System.Enum) ||
618 typeFrom == typeof(System.ValueType) ||
619 TypeUtils.IsLegalExplicitVariantDelegateConversion(typeFrom, typeTo))
621 il.EmitCastToType(typeFrom, typeTo);
622 } else if (isTypeFromNullable || isTypeToNullable) {
623 il.EmitNullableConversion(typeFrom, typeTo, isChecked);
624 } else if (!(TypeUtils.IsConvertible(typeFrom) && TypeUtils.IsConvertible(typeTo)) // primitive runtime conversion
626 (nnExprType.IsAssignableFrom(nnType) || // down cast
627 nnType.IsAssignableFrom(nnExprType))) // up cast
629 il.EmitCastToType(typeFrom, typeTo);
630 } else if (typeFrom.IsArray && typeTo.IsArray) {
631 // See DevDiv Bugs #94657.
632 il.EmitCastToType(typeFrom, typeTo);
634 il.EmitNumericConversion(typeFrom, typeTo, isChecked);
639 private static void EmitCastToType(this ILGenerator il, Type typeFrom, Type typeTo) {
640 if (!typeFrom.IsValueType && typeTo.IsValueType) {
641 il.Emit(OpCodes.Unbox_Any, typeTo);
642 } else if (typeFrom.IsValueType && !typeTo.IsValueType) {
643 il.Emit(OpCodes.Box, typeFrom);
644 if (typeTo != typeof(object)) {
645 il.Emit(OpCodes.Castclass, typeTo);
647 } else if (!typeFrom.IsValueType && !typeTo.IsValueType) {
648 il.Emit(OpCodes.Castclass, typeTo);
650 throw Error.InvalidCast(typeFrom, typeTo);
655 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
656 private static void EmitNumericConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
657 bool isFromUnsigned = TypeUtils.IsUnsigned(typeFrom);
658 bool isFromFloatingPoint = TypeUtils.IsFloatingPoint(typeFrom);
659 if (typeTo == typeof(Single)) {
661 il.Emit(OpCodes.Conv_R_Un);
662 il.Emit(OpCodes.Conv_R4);
663 } else if (typeTo == typeof(Double)) {
665 il.Emit(OpCodes.Conv_R_Un);
666 il.Emit(OpCodes.Conv_R8);
668 TypeCode tc = Type.GetTypeCode(typeTo);
670 // Overflow checking needs to know if the source value on the IL stack is unsigned or not.
671 if (isFromUnsigned) {
674 il.Emit(OpCodes.Conv_Ovf_I1_Un);
677 il.Emit(OpCodes.Conv_Ovf_I2_Un);
680 il.Emit(OpCodes.Conv_Ovf_I4_Un);
683 il.Emit(OpCodes.Conv_Ovf_I8_Un);
686 il.Emit(OpCodes.Conv_Ovf_U1_Un);
688 case TypeCode.UInt16:
690 il.Emit(OpCodes.Conv_Ovf_U2_Un);
692 case TypeCode.UInt32:
693 il.Emit(OpCodes.Conv_Ovf_U4_Un);
695 case TypeCode.UInt64:
696 il.Emit(OpCodes.Conv_Ovf_U8_Un);
699 throw Error.UnhandledConvert(typeTo);
704 il.Emit(OpCodes.Conv_Ovf_I1);
707 il.Emit(OpCodes.Conv_Ovf_I2);
710 il.Emit(OpCodes.Conv_Ovf_I4);
713 il.Emit(OpCodes.Conv_Ovf_I8);
716 il.Emit(OpCodes.Conv_Ovf_U1);
718 case TypeCode.UInt16:
720 il.Emit(OpCodes.Conv_Ovf_U2);
722 case TypeCode.UInt32:
723 il.Emit(OpCodes.Conv_Ovf_U4);
725 case TypeCode.UInt64:
726 il.Emit(OpCodes.Conv_Ovf_U8);
729 throw Error.UnhandledConvert(typeTo);
735 il.Emit(OpCodes.Conv_I1);
738 il.Emit(OpCodes.Conv_U1);
741 il.Emit(OpCodes.Conv_I2);
743 case TypeCode.UInt16:
745 il.Emit(OpCodes.Conv_U2);
748 il.Emit(OpCodes.Conv_I4);
750 case TypeCode.UInt32:
751 il.Emit(OpCodes.Conv_U4);
754 if (isFromUnsigned) {
755 il.Emit(OpCodes.Conv_U8);
757 il.Emit(OpCodes.Conv_I8);
760 case TypeCode.UInt64:
761 if (isFromUnsigned || isFromFloatingPoint) {
762 il.Emit(OpCodes.Conv_U8);
764 il.Emit(OpCodes.Conv_I8);
768 throw Error.UnhandledConvert(typeTo);
774 private static void EmitNullableToNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
775 Debug.Assert(TypeUtils.IsNullableType(typeFrom));
776 Debug.Assert(TypeUtils.IsNullableType(typeTo));
777 Label labIfNull = default(Label);
778 Label labEnd = default(Label);
779 LocalBuilder locFrom = null;
780 LocalBuilder locTo = null;
781 locFrom = il.DeclareLocal(typeFrom);
782 il.Emit(OpCodes.Stloc, locFrom);
783 locTo = il.DeclareLocal(typeTo);
785 il.Emit(OpCodes.Ldloca, locFrom);
786 il.EmitHasValue(typeFrom);
787 labIfNull = il.DefineLabel();
788 il.Emit(OpCodes.Brfalse_S, labIfNull);
789 il.Emit(OpCodes.Ldloca, locFrom);
790 il.EmitGetValueOrDefault(typeFrom);
791 Type nnTypeFrom = TypeUtils.GetNonNullableType(typeFrom);
792 Type nnTypeTo = TypeUtils.GetNonNullableType(typeTo);
793 il.EmitConvertToType(nnTypeFrom, nnTypeTo, isChecked);
794 // construct result type
795 ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo });
796 il.Emit(OpCodes.Newobj, ci);
797 il.Emit(OpCodes.Stloc, locTo);
798 labEnd = il.DefineLabel();
799 il.Emit(OpCodes.Br_S, labEnd);
800 // if null then create a default one
801 il.MarkLabel(labIfNull);
802 il.Emit(OpCodes.Ldloca, locTo);
803 il.Emit(OpCodes.Initobj, typeTo);
804 il.MarkLabel(labEnd);
805 il.Emit(OpCodes.Ldloc, locTo);
809 private static void EmitNonNullableToNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
810 Debug.Assert(!TypeUtils.IsNullableType(typeFrom));
811 Debug.Assert(TypeUtils.IsNullableType(typeTo));
812 LocalBuilder locTo = null;
813 locTo = il.DeclareLocal(typeTo);
814 Type nnTypeTo = TypeUtils.GetNonNullableType(typeTo);
815 il.EmitConvertToType(typeFrom, nnTypeTo, isChecked);
816 ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo });
817 il.Emit(OpCodes.Newobj, ci);
818 il.Emit(OpCodes.Stloc, locTo);
819 il.Emit(OpCodes.Ldloc, locTo);
823 private static void EmitNullableToNonNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
824 Debug.Assert(TypeUtils.IsNullableType(typeFrom));
825 Debug.Assert(!TypeUtils.IsNullableType(typeTo));
826 if (typeTo.IsValueType)
827 il.EmitNullableToNonNullableStructConversion(typeFrom, typeTo, isChecked);
829 il.EmitNullableToReferenceConversion(typeFrom);
833 private static void EmitNullableToNonNullableStructConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
834 Debug.Assert(TypeUtils.IsNullableType(typeFrom));
835 Debug.Assert(!TypeUtils.IsNullableType(typeTo));
836 Debug.Assert(typeTo.IsValueType);
837 LocalBuilder locFrom = null;
838 locFrom = il.DeclareLocal(typeFrom);
839 il.Emit(OpCodes.Stloc, locFrom);
840 il.Emit(OpCodes.Ldloca, locFrom);
841 il.EmitGetValue(typeFrom);
842 Type nnTypeFrom = TypeUtils.GetNonNullableType(typeFrom);
843 il.EmitConvertToType(nnTypeFrom, typeTo, isChecked);
847 private static void EmitNullableToReferenceConversion(this ILGenerator il, Type typeFrom) {
848 Debug.Assert(TypeUtils.IsNullableType(typeFrom));
849 // We've got a conversion from nullable to Object, ValueType, Enum, etc. Just box it so that
850 // we get the nullable semantics.
851 il.Emit(OpCodes.Box, typeFrom);
855 private static void EmitNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
856 bool isTypeFromNullable = TypeUtils.IsNullableType(typeFrom);
857 bool isTypeToNullable = TypeUtils.IsNullableType(typeTo);
858 Debug.Assert(isTypeFromNullable || isTypeToNullable);
859 if (isTypeFromNullable && isTypeToNullable)
860 il.EmitNullableToNullableConversion(typeFrom, typeTo, isChecked);
861 else if (isTypeFromNullable)
862 il.EmitNullableToNonNullableConversion(typeFrom, typeTo, isChecked);
864 il.EmitNonNullableToNullableConversion(typeFrom, typeTo, isChecked);
868 internal static void EmitHasValue(this ILGenerator il, Type nullableType) {
869 MethodInfo mi = nullableType.GetMethod("get_HasValue", BindingFlags.Instance | BindingFlags.Public);
870 Debug.Assert(nullableType.IsValueType);
871 il.Emit(OpCodes.Call, mi);
875 internal static void EmitGetValue(this ILGenerator il, Type nullableType) {
876 MethodInfo mi = nullableType.GetMethod("get_Value", BindingFlags.Instance | BindingFlags.Public);
877 Debug.Assert(nullableType.IsValueType);
878 il.Emit(OpCodes.Call, mi);
882 internal static void EmitGetValueOrDefault(this ILGenerator il, Type nullableType) {
883 MethodInfo mi = nullableType.GetMethod("GetValueOrDefault", ReflectionUtils.EmptyTypes);
884 Debug.Assert(nullableType.IsValueType);
885 il.Emit(OpCodes.Call, mi);
893 /// Emits an array of constant values provided in the given list.
894 /// The array is strongly typed.
896 internal static void EmitArray<T>(this ILGenerator il, IList<T> items) {
897 ContractUtils.RequiresNotNull(items, "items");
899 il.EmitInt(items.Count);
900 il.Emit(OpCodes.Newarr, typeof(T));
901 for (int i = 0; i < items.Count; i++) {
902 il.Emit(OpCodes.Dup);
904 il.EmitConstant(items[i], typeof(T));
905 il.EmitStoreElement(typeof(T));
910 /// Emits an array of values of count size. The items are emitted via the callback
911 /// which is provided with the current item index to emit.
913 internal static void EmitArray(this ILGenerator il, Type elementType, int count, Action<int> emit) {
914 ContractUtils.RequiresNotNull(elementType, "elementType");
915 ContractUtils.RequiresNotNull(emit, "emit");
916 if (count < 0) throw Error.CountCannotBeNegative();
919 il.Emit(OpCodes.Newarr, elementType);
920 for (int i = 0; i < count; i++) {
921 il.Emit(OpCodes.Dup);
926 il.EmitStoreElement(elementType);
931 /// Emits an array construction code.
932 /// The code assumes that bounds for all dimensions
933 /// are already emitted.
935 internal static void EmitArray(this ILGenerator il, Type arrayType) {
936 ContractUtils.RequiresNotNull(arrayType, "arrayType");
937 if (!arrayType.IsArray) throw Error.ArrayTypeMustBeArray();
939 int rank = arrayType.GetArrayRank();
941 il.Emit(OpCodes.Newarr, arrayType.GetElementType());
943 Type[] types = new Type[rank];
944 for (int i = 0; i < rank; i++) {
945 types[i] = typeof(int);
947 il.EmitNew(arrayType, types);
953 #region Support for emitting constants
955 internal static void EmitDecimal(this ILGenerator il, decimal value) {
956 if (Decimal.Truncate(value) == value) {
957 if (Int32.MinValue <= value && value <= Int32.MaxValue) {
958 int intValue = Decimal.ToInt32(value);
959 il.EmitInt(intValue);
960 il.EmitNew(typeof(Decimal).GetConstructor(new Type[] { typeof(int) }));
961 } else if (Int64.MinValue <= value && value <= Int64.MaxValue) {
962 long longValue = Decimal.ToInt64(value);
963 il.EmitLong(longValue);
964 il.EmitNew(typeof(Decimal).GetConstructor(new Type[] { typeof(long) }));
966 il.EmitDecimalBits(value);
969 il.EmitDecimalBits(value);
973 private static void EmitDecimalBits(this ILGenerator il, decimal value) {
974 int[] bits = Decimal.GetBits(value);
978 il.EmitBoolean((bits[3] & 0x80000000) != 0);
979 il.EmitByte((byte)(bits[3] >> 16));
980 il.EmitNew(typeof(decimal).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int), typeof(bool), typeof(byte) }));
985 /// Semantics match C# compiler behavior
987 internal static void EmitDefault(this ILGenerator il, Type type) {
988 switch (Type.GetTypeCode(type)) {
989 case TypeCode.Object:
990 case TypeCode.DateTime:
991 if (type.IsValueType) {
992 // Type.GetTypeCode on an enum returns the underlying
993 // integer TypeCode, so we won't get here.
994 Debug.Assert(!type.IsEnum);
996 // This is the IL for default(T) if T is a generic type
997 // parameter, so it should work for any type. It's also
998 // the standard pattern for structs.
999 LocalBuilder lb = il.DeclareLocal(type);
1000 il.Emit(OpCodes.Ldloca, lb);
1001 il.Emit(OpCodes.Initobj, type);
1002 il.Emit(OpCodes.Ldloc, lb);
1004 il.Emit(OpCodes.Ldnull);
1008 case TypeCode.Empty:
1009 case TypeCode.String:
1010 case TypeCode.DBNull:
1011 il.Emit(OpCodes.Ldnull);
1014 case TypeCode.Boolean:
1016 case TypeCode.SByte:
1018 case TypeCode.Int16:
1019 case TypeCode.UInt16:
1020 case TypeCode.Int32:
1021 case TypeCode.UInt32:
1022 il.Emit(OpCodes.Ldc_I4_0);
1025 case TypeCode.Int64:
1026 case TypeCode.UInt64:
1027 il.Emit(OpCodes.Ldc_I4_0);
1028 il.Emit(OpCodes.Conv_I8);
1031 case TypeCode.Single:
1032 il.Emit(OpCodes.Ldc_R4, default(Single));
1035 case TypeCode.Double:
1036 il.Emit(OpCodes.Ldc_R8, default(Double));
1039 case TypeCode.Decimal:
1040 il.Emit(OpCodes.Ldc_I4_0);
1041 il.Emit(OpCodes.Newobj, typeof(Decimal).GetConstructor(new Type[] { typeof(int) }));
1045 throw ContractUtils.Unreachable;