Merge pull request #347 from JamesB7/master
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Compiler / ILGen.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
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.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 using System;
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;
24
25 #if !FEATURE_CORE_DLR
26 namespace Microsoft.Scripting.Ast.Compiler {
27 #else
28 namespace System.Linq.Expressions.Compiler {
29 #endif
30 #if !FEATURE_CORE_DLR || SILVERLIGHT
31     using ILGenerator = OffsetTrackingILGenerator;
32 #endif
33
34     internal static class ILGen {
35
36         internal static void Emit(this ILGenerator il, OpCode opcode, MethodBase methodBase) {
37             Debug.Assert(methodBase is MethodInfo || methodBase is ConstructorInfo);
38
39             if (methodBase.MemberType == MemberTypes.Constructor) {
40                 il.Emit(opcode, (ConstructorInfo)methodBase);
41             } else {
42                 il.Emit(opcode, (MethodInfo)methodBase);
43             }
44         }
45
46         #region Instruction helpers
47
48         internal static void EmitLoadArg(this ILGenerator il, int index) {
49             Debug.Assert(index >= 0);
50
51             switch (index) {
52                 case 0:
53                     il.Emit(OpCodes.Ldarg_0);
54                     break;
55                 case 1:
56                     il.Emit(OpCodes.Ldarg_1);
57                     break;
58                 case 2:
59                     il.Emit(OpCodes.Ldarg_2);
60                     break;
61                 case 3:
62                     il.Emit(OpCodes.Ldarg_3);
63                     break;
64                 default:
65                     if (index <= Byte.MaxValue) {
66                         il.Emit(OpCodes.Ldarg_S, (byte)index);
67                     } else {
68                         il.Emit(OpCodes.Ldarg, index);
69                     }
70                     break;
71             }
72         }
73
74         internal static void EmitLoadArgAddress(this ILGenerator il, int index) {
75             Debug.Assert(index >= 0);
76
77             if (index <= Byte.MaxValue) {
78                 il.Emit(OpCodes.Ldarga_S, (byte)index);
79             } else {
80                 il.Emit(OpCodes.Ldarga, index);
81             }
82         }
83
84         internal static void EmitStoreArg(this ILGenerator il, int index) {
85             Debug.Assert(index >= 0);
86
87             if (index <= Byte.MaxValue) {
88                 il.Emit(OpCodes.Starg_S, (byte)index);
89             } else {
90                 il.Emit(OpCodes.Starg, index);
91             }
92         }
93
94         /// <summary>
95         /// Emits a Ldind* instruction for the appropriate type
96         /// </summary>
97         internal static void EmitLoadValueIndirect(this ILGenerator il, Type type) {
98             ContractUtils.RequiresNotNull(type, "type");
99
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);
119                 } else {
120                     il.Emit(OpCodes.Ldobj, type);
121                 }
122             } else {
123                 il.Emit(OpCodes.Ldind_Ref);
124             }
125         }
126
127
128         /// <summary>
129         /// Emits a Stind* instruction for the appropriate type.
130         /// </summary>
131         internal static void EmitStoreValueIndirect(this ILGenerator il, Type type) {
132             ContractUtils.RequiresNotNull(type, "type");
133
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);
149                 } else {
150                     il.Emit(OpCodes.Stobj, type);
151                 }
152             } else {
153                 il.Emit(OpCodes.Stind_Ref);
154             }
155         }
156
157         // Emits the Ldelem* instruction for the appropriate type
158
159         internal static void EmitLoadElement(this ILGenerator il, Type type) {
160             ContractUtils.RequiresNotNull(type, "type");
161
162             if (!type.IsValueType) {
163                 il.Emit(OpCodes.Ldelem_Ref);
164             } else if (type.IsEnum) {
165                 il.Emit(OpCodes.Ldelem, type);
166             } else {
167                 switch (Type.GetTypeCode(type)) {
168                     case TypeCode.Boolean:
169                     case TypeCode.SByte:
170                         il.Emit(OpCodes.Ldelem_I1);
171                         break;
172                     case TypeCode.Byte:
173                         il.Emit(OpCodes.Ldelem_U1);
174                         break;
175                     case TypeCode.Int16:
176                         il.Emit(OpCodes.Ldelem_I2);
177                         break;
178                     case TypeCode.Char:
179                     case TypeCode.UInt16:
180                         il.Emit(OpCodes.Ldelem_U2);
181                         break;
182                     case TypeCode.Int32:
183                         il.Emit(OpCodes.Ldelem_I4);
184                         break;
185                     case TypeCode.UInt32:
186                         il.Emit(OpCodes.Ldelem_U4);
187                         break;
188                     case TypeCode.Int64:
189                     case TypeCode.UInt64:
190                         il.Emit(OpCodes.Ldelem_I8);
191                         break;
192                     case TypeCode.Single:
193                         il.Emit(OpCodes.Ldelem_R4);
194                         break;
195                     case TypeCode.Double:
196                         il.Emit(OpCodes.Ldelem_R8);
197                         break;
198                     default:
199                         il.Emit(OpCodes.Ldelem, type);
200                         break;
201                 }
202             }
203         }
204
205         /// <summary>
206         /// Emits a Stelem* instruction for the appropriate type.
207         /// </summary>
208         internal static void EmitStoreElement(this ILGenerator il, Type type) {
209             ContractUtils.RequiresNotNull(type, "type");
210
211             if (type.IsEnum) {
212                 il.Emit(OpCodes.Stelem, type);
213                 return;
214             }
215             switch (Type.GetTypeCode(type)) {
216                 case TypeCode.Boolean:
217                 case TypeCode.SByte:
218                 case TypeCode.Byte:
219                     il.Emit(OpCodes.Stelem_I1);
220                     break;
221                 case TypeCode.Char:
222                 case TypeCode.Int16:
223                 case TypeCode.UInt16:
224                     il.Emit(OpCodes.Stelem_I2);
225                     break;
226                 case TypeCode.Int32:
227                 case TypeCode.UInt32:
228                     il.Emit(OpCodes.Stelem_I4);
229                     break;
230                 case TypeCode.Int64:
231                 case TypeCode.UInt64:
232                     il.Emit(OpCodes.Stelem_I8);
233                     break;
234                 case TypeCode.Single:
235                     il.Emit(OpCodes.Stelem_R4);
236                     break;
237                 case TypeCode.Double:
238                     il.Emit(OpCodes.Stelem_R8);
239                     break;
240                 default:
241                     if (type.IsValueType) {
242                         il.Emit(OpCodes.Stelem, type);
243                     } else {
244                         il.Emit(OpCodes.Stelem_Ref);
245                     }
246                     break;
247             }
248         }
249
250         internal static void EmitType(this ILGenerator il, Type type) {
251             ContractUtils.RequiresNotNull(type, "type");
252
253             il.Emit(OpCodes.Ldtoken, type);
254             il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
255         }
256
257         #endregion
258
259         #region Fields, properties and methods
260
261         internal static void EmitFieldAddress(this ILGenerator il, FieldInfo fi) {
262             ContractUtils.RequiresNotNull(fi, "fi");
263
264             if (fi.IsStatic) {
265                 il.Emit(OpCodes.Ldsflda, fi);
266             } else {
267                 il.Emit(OpCodes.Ldflda, fi);
268             }
269         }
270
271         internal static void EmitFieldGet(this ILGenerator il, FieldInfo fi) {
272             ContractUtils.RequiresNotNull(fi, "fi");
273
274             if (fi.IsStatic) {
275                 il.Emit(OpCodes.Ldsfld, fi);
276             } else {
277                 il.Emit(OpCodes.Ldfld, fi);
278             }
279         }
280
281         internal static void EmitFieldSet(this ILGenerator il, FieldInfo fi) {
282             ContractUtils.RequiresNotNull(fi, "fi");
283
284             if (fi.IsStatic) {
285                 il.Emit(OpCodes.Stsfld, fi);
286             } else {
287                 il.Emit(OpCodes.Stfld, fi);
288             }
289         }
290
291         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
292         internal static void EmitNew(this ILGenerator il, ConstructorInfo ci) {
293             ContractUtils.RequiresNotNull(ci, "ci");
294
295             if (ci.DeclaringType.ContainsGenericParameters) {
296                 throw Error.IllegalNewGenericParams(ci.DeclaringType);
297             }
298
299             il.Emit(OpCodes.Newobj, ci);
300         }
301
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");
306
307             ConstructorInfo ci = type.GetConstructor(paramTypes);
308             if (ci == null) throw Error.TypeDoesNotHaveConstructorForTheSignature();
309             il.EmitNew(ci);
310         }
311
312         #endregion
313
314         #region Constants
315
316         internal static void EmitNull(this ILGenerator il) {
317             il.Emit(OpCodes.Ldnull);
318         }
319
320         internal static void EmitString(this ILGenerator il, string value) {
321             ContractUtils.RequiresNotNull(value, "value");
322             il.Emit(OpCodes.Ldstr, value);
323         }
324
325         internal static void EmitBoolean(this ILGenerator il, bool value) {
326             if (value) {
327                 il.Emit(OpCodes.Ldc_I4_1);
328             } else {
329                 il.Emit(OpCodes.Ldc_I4_0);
330             }
331         }
332
333         internal static void EmitChar(this ILGenerator il, char value) {
334             il.EmitInt(value);
335             il.Emit(OpCodes.Conv_U2);
336         }
337
338         internal static void EmitByte(this ILGenerator il, byte value) {
339             il.EmitInt(value);
340             il.Emit(OpCodes.Conv_U1);
341         }
342
343         internal static void EmitSByte(this ILGenerator il, sbyte value) {
344             il.EmitInt(value);
345             il.Emit(OpCodes.Conv_I1);
346         }
347
348         internal static void EmitShort(this ILGenerator il, short value) {
349             il.EmitInt(value);
350             il.Emit(OpCodes.Conv_I2);
351         }
352
353         internal static void EmitUShort(this ILGenerator il, ushort value) {
354             il.EmitInt(value);
355             il.Emit(OpCodes.Conv_U2);
356         }
357
358         internal static void EmitInt(this ILGenerator il, int value) {
359             OpCode c;
360             switch (value) {
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 >= -128 && value <= 127) {
393                         il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
394                     } else {
395                         il.Emit(OpCodes.Ldc_I4, value);
396                     }
397                     return;
398             }
399             il.Emit(c);
400         }
401
402         internal static void EmitUInt(this ILGenerator il, uint value) {
403             il.EmitInt((int)value);
404             il.Emit(OpCodes.Conv_U4);
405         }
406
407         internal static void EmitLong(this ILGenerator il, long value) {
408             il.Emit(OpCodes.Ldc_I8, value);
409
410             //
411             // Now, emit convert to give the constant type information.
412             //
413             // Otherwise, it is treated as unsigned and overflow is not
414             // detected if it's used in checked ops.
415             //
416             il.Emit(OpCodes.Conv_I8);
417         }
418
419         internal static void EmitULong(this ILGenerator il, ulong value) {
420             il.Emit(OpCodes.Ldc_I8, (long)value);
421             il.Emit(OpCodes.Conv_U8);
422         }
423
424         internal static void EmitDouble(this ILGenerator il, double value) {
425             il.Emit(OpCodes.Ldc_R8, value);
426         }
427
428         internal static void EmitSingle(this ILGenerator il, float value) {
429             il.Emit(OpCodes.Ldc_R4, value);
430         }
431
432         // matches TryEmitConstant
433         internal static bool CanEmitConstant(object value, Type type) {
434             if (value == null || CanEmitILConstant(type)) {
435                 return true;
436             }
437
438             Type t = value as Type;
439             if (t != null && ShouldLdtoken(t)) {
440                 return true;
441             }
442
443             MethodBase mb = value as MethodBase;
444             if (mb != null && ShouldLdtoken(mb)) {
445                 return true;
446             }
447
448             return false;
449         }
450
451         // matches TryEmitILConstant
452         private static bool CanEmitILConstant(Type type) {
453             switch (Type.GetTypeCode(type)) {
454                 case TypeCode.Boolean:
455                 case TypeCode.SByte:
456                 case TypeCode.Int16:
457                 case TypeCode.Int32:
458                 case TypeCode.Int64:
459                 case TypeCode.Single:
460                 case TypeCode.Double:
461                 case TypeCode.Char:
462                 case TypeCode.Byte:
463                 case TypeCode.UInt16:
464                 case TypeCode.UInt32:
465                 case TypeCode.UInt64:
466                 case TypeCode.Decimal:
467                 case TypeCode.String:
468                     return true;
469             }
470             return false;
471         }
472
473         internal static void EmitConstant(this ILGenerator il, object value) {
474             Debug.Assert(value != null);
475             EmitConstant(il, value, value.GetType());
476         }
477
478
479         //
480         // Note: we support emitting more things as IL constants than
481         // Linq does
482         internal static void EmitConstant(this ILGenerator il, object value, Type type) {
483             if (value == null) {
484                 // Smarter than the Linq implementation which uses the initobj
485                 // pattern for all value types (works, but requires a local and
486                 // more IL)
487                 il.EmitDefault(type);
488                 return;
489             }
490
491             // Handle the easy cases
492             if (il.TryEmitILConstant(value, type)) {
493                 return;
494             }
495
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)) {
499                 il.EmitType(t);
500                 if (type != typeof(Type)) {
501                     il.Emit(OpCodes.Castclass, type);
502                 }
503                 return;
504             }
505
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) }));
513                 } else {
514                     il.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetMethodFromHandle", new Type[] { typeof(RuntimeMethodHandle) }));
515                 }
516                 if (type != typeof(MethodBase)) {
517                     il.Emit(OpCodes.Castclass, type);
518                 }
519                 return;
520             }
521
522             throw ContractUtils.Unreachable;
523         }
524
525         internal static bool ShouldLdtoken(Type t) {
526 #if FEATURE_REFEMIT
527             if (t is TypeBuilder) {
528                 return true;
529             }
530 #endif
531             return t.IsGenericParameter || t.IsVisible;
532         }
533
534         internal static bool ShouldLdtoken(MethodBase mb) {
535             // Can't ldtoken on a DynamicMethod
536             if (mb is DynamicMethod) {
537                 return false;
538             }
539
540             Type dt = mb.DeclaringType;
541             return dt == null || ShouldLdtoken(dt);
542         }
543
544
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);
549                     return true;
550                 case TypeCode.SByte:
551                     il.EmitSByte((sbyte)value);
552                     return true;
553                 case TypeCode.Int16:
554                     il.EmitShort((short)value);
555                     return true;
556                 case TypeCode.Int32:
557                     il.EmitInt((int)value);
558                     return true;
559                 case TypeCode.Int64:
560                     il.EmitLong((long)value);
561                     return true;
562                 case TypeCode.Single:
563                     il.EmitSingle((float)value);
564                     return true;
565                 case TypeCode.Double:
566                     il.EmitDouble((double)value);
567                     return true;
568                 case TypeCode.Char:
569                     il.EmitChar((char)value);
570                     return true;
571                 case TypeCode.Byte:
572                     il.EmitByte((byte)value);
573                     return true;
574                 case TypeCode.UInt16:
575                     il.EmitUShort((ushort)value);
576                     return true;
577                 case TypeCode.UInt32:
578                     il.EmitUInt((uint)value);
579                     return true;
580                 case TypeCode.UInt64:
581                     il.EmitULong((ulong)value);
582                     return true;
583                 case TypeCode.Decimal:
584                     il.EmitDecimal((decimal)value);
585                     return true;
586                 case TypeCode.String:
587                     il.EmitString((string)value);
588                     return true;
589                 default:
590                     return false;
591             }
592         }
593
594         #endregion
595
596         #region Linq Conversions
597
598         internal static void EmitConvertToType(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
599             if (TypeUtils.AreEquivalent(typeFrom, typeTo)) {
600                 return;
601             }
602
603             if (typeFrom == typeof(void) || typeTo == typeof(void)) {
604                 throw ContractUtils.Unreachable;
605             }
606
607             bool isTypeFromNullable = TypeUtils.IsNullableType(typeFrom);
608             bool isTypeToNullable = TypeUtils.IsNullableType(typeTo);
609
610             Type nnExprType = TypeUtils.GetNonNullableType(typeFrom);
611             Type nnType = TypeUtils.GetNonNullableType(typeTo);
612
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))
620             {
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
625                        &&
626                        (nnExprType.IsAssignableFrom(nnType) || // down cast
627                        nnType.IsAssignableFrom(nnExprType))) // up cast
628             {
629                 il.EmitCastToType(typeFrom, typeTo);
630             } else if (typeFrom.IsArray && typeTo.IsArray) {
631                 // See DevDiv Bugs #94657.
632                 il.EmitCastToType(typeFrom, typeTo);
633             } else {
634                 il.EmitNumericConversion(typeFrom, typeTo, isChecked);
635             }
636         }
637
638
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);
646                 }
647             } else if (!typeFrom.IsValueType && !typeTo.IsValueType) {
648                 il.Emit(OpCodes.Castclass, typeTo);
649             } else {
650                 throw Error.InvalidCast(typeFrom, typeTo);
651             }
652         }
653
654
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)) {
660                 if (isFromUnsigned)
661                     il.Emit(OpCodes.Conv_R_Un);
662                 il.Emit(OpCodes.Conv_R4);
663             } else if (typeTo == typeof(Double)) {
664                 if (isFromUnsigned)
665                     il.Emit(OpCodes.Conv_R_Un);
666                 il.Emit(OpCodes.Conv_R8);
667             } else {
668                 TypeCode tc = Type.GetTypeCode(typeTo);
669                 if (isChecked) {
670                     // Overflow checking needs to know if the source value on the IL stack is unsigned or not.
671                     if (isFromUnsigned) {
672                         switch (tc) {
673                             case TypeCode.SByte:
674                                 il.Emit(OpCodes.Conv_Ovf_I1_Un);
675                                 break;
676                             case TypeCode.Int16:
677                                 il.Emit(OpCodes.Conv_Ovf_I2_Un);
678                                 break;
679                             case TypeCode.Int32:
680                                 il.Emit(OpCodes.Conv_Ovf_I4_Un);
681                                 break;
682                             case TypeCode.Int64:
683                                 il.Emit(OpCodes.Conv_Ovf_I8_Un);
684                                 break;
685                             case TypeCode.Byte:
686                                 il.Emit(OpCodes.Conv_Ovf_U1_Un);
687                                 break;
688                             case TypeCode.UInt16:
689                             case TypeCode.Char:
690                                 il.Emit(OpCodes.Conv_Ovf_U2_Un);
691                                 break;
692                             case TypeCode.UInt32:
693                                 il.Emit(OpCodes.Conv_Ovf_U4_Un);
694                                 break;
695                             case TypeCode.UInt64:
696                                 il.Emit(OpCodes.Conv_Ovf_U8_Un);
697                                 break;
698                             default:
699                                 throw Error.UnhandledConvert(typeTo);
700                         }
701                     } else {
702                         switch (tc) {
703                             case TypeCode.SByte:
704                                 il.Emit(OpCodes.Conv_Ovf_I1);
705                                 break;
706                             case TypeCode.Int16:
707                                 il.Emit(OpCodes.Conv_Ovf_I2);
708                                 break;
709                             case TypeCode.Int32:
710                                 il.Emit(OpCodes.Conv_Ovf_I4);
711                                 break;
712                             case TypeCode.Int64:
713                                 il.Emit(OpCodes.Conv_Ovf_I8);
714                                 break;
715                             case TypeCode.Byte:
716                                 il.Emit(OpCodes.Conv_Ovf_U1);
717                                 break;
718                             case TypeCode.UInt16:
719                             case TypeCode.Char:
720                                 il.Emit(OpCodes.Conv_Ovf_U2);
721                                 break;
722                             case TypeCode.UInt32:
723                                 il.Emit(OpCodes.Conv_Ovf_U4);
724                                 break;
725                             case TypeCode.UInt64:
726                                 il.Emit(OpCodes.Conv_Ovf_U8);
727                                 break;
728                             default:
729                                 throw Error.UnhandledConvert(typeTo);
730                         }
731                     }
732                 } else {
733                     switch (tc) {
734                         case TypeCode.SByte:
735                             il.Emit(OpCodes.Conv_I1);
736                             break;
737                         case TypeCode.Byte:
738                             il.Emit(OpCodes.Conv_U1);
739                             break;
740                         case TypeCode.Int16:
741                             il.Emit(OpCodes.Conv_I2);
742                             break;
743                         case TypeCode.UInt16:
744                         case TypeCode.Char:
745                             il.Emit(OpCodes.Conv_U2);
746                             break;
747                         case TypeCode.Int32:
748                             il.Emit(OpCodes.Conv_I4);
749                             break;
750                         case TypeCode.UInt32:
751                             il.Emit(OpCodes.Conv_U4);
752                             break;
753                         case TypeCode.Int64:
754                             if (isFromUnsigned) {
755                                 il.Emit(OpCodes.Conv_U8);
756                             } else {
757                                 il.Emit(OpCodes.Conv_I8);
758                             }
759                             break;
760                         case TypeCode.UInt64:
761                             if (isFromUnsigned || isFromFloatingPoint) {
762                                 il.Emit(OpCodes.Conv_U8);
763                             } else {
764                                 il.Emit(OpCodes.Conv_I8);
765                             }
766                             break;
767                         default:
768                             throw Error.UnhandledConvert(typeTo);
769                     }
770                 }
771             }
772         }
773
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);
784             // test for null
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);
806         }
807
808
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);
820         }
821
822
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);
828             else
829                 il.EmitNullableToReferenceConversion(typeFrom);
830         }
831
832
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);
844         }
845
846
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);
852         }
853
854
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);
863             else
864                 il.EmitNonNullableToNullableConversion(typeFrom, typeTo, isChecked);
865         }
866
867
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);
872         }
873
874
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);
879         }
880
881
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);
886         }
887
888         #endregion
889
890         #region Arrays
891
892         /// <summary>
893         /// Emits an array of constant values provided in the given list.
894         /// The array is strongly typed.
895         /// </summary>
896         internal static void EmitArray<T>(this ILGenerator il, IList<T> items) {
897             ContractUtils.RequiresNotNull(items, "items");
898
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);
903                 il.EmitInt(i);
904                 il.EmitConstant(items[i], typeof(T));
905                 il.EmitStoreElement(typeof(T));
906             }
907         }
908
909         /// <summary>
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.
912         /// </summary>
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();
917
918             il.EmitInt(count);
919             il.Emit(OpCodes.Newarr, elementType);
920             for (int i = 0; i < count; i++) {
921                 il.Emit(OpCodes.Dup);
922                 il.EmitInt(i);
923
924                 emit(i);
925
926                 il.EmitStoreElement(elementType);
927             }
928         }
929
930         /// <summary>
931         /// Emits an array construction code.  
932         /// The code assumes that bounds for all dimensions
933         /// are already emitted.
934         /// </summary>
935         internal static void EmitArray(this ILGenerator il, Type arrayType) {
936             ContractUtils.RequiresNotNull(arrayType, "arrayType");
937             if (!arrayType.IsArray) throw Error.ArrayTypeMustBeArray();
938
939             int rank = arrayType.GetArrayRank();
940             if (rank == 1) {
941                 il.Emit(OpCodes.Newarr, arrayType.GetElementType());
942             } else {
943                 Type[] types = new Type[rank];
944                 for (int i = 0; i < rank; i++) {
945                     types[i] = typeof(int);
946                 }
947                 il.EmitNew(arrayType, types);
948             }
949         }
950
951         #endregion
952
953         #region Support for emitting constants
954
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) }));
965                 } else {
966                     il.EmitDecimalBits(value);
967                 }
968             } else {
969                 il.EmitDecimalBits(value);
970             }
971         }
972
973         private static void EmitDecimalBits(this ILGenerator il, decimal value) {
974             int[] bits = Decimal.GetBits(value);
975             il.EmitInt(bits[0]);
976             il.EmitInt(bits[1]);
977             il.EmitInt(bits[2]);
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) }));
981         }
982
983         /// <summary>
984         /// Emits default(T)
985         /// Semantics match C# compiler behavior
986         /// </summary>
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);
995
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);
1003                     } else {
1004                         il.Emit(OpCodes.Ldnull);
1005                     }
1006                     break;
1007
1008                 case TypeCode.Empty:
1009                 case TypeCode.String:
1010                 case TypeCode.DBNull:
1011                     il.Emit(OpCodes.Ldnull);
1012                     break;
1013
1014                 case TypeCode.Boolean:
1015                 case TypeCode.Char:
1016                 case TypeCode.SByte:
1017                 case TypeCode.Byte:
1018                 case TypeCode.Int16:
1019                 case TypeCode.UInt16:
1020                 case TypeCode.Int32:
1021                 case TypeCode.UInt32:
1022                     il.Emit(OpCodes.Ldc_I4_0);
1023                     break;
1024
1025                 case TypeCode.Int64:
1026                 case TypeCode.UInt64:
1027                     il.Emit(OpCodes.Ldc_I4_0);
1028                     il.Emit(OpCodes.Conv_I8);
1029                     break;
1030
1031                 case TypeCode.Single:
1032                     il.Emit(OpCodes.Ldc_R4, default(Single));
1033                     break;
1034
1035                 case TypeCode.Double:
1036                     il.Emit(OpCodes.Ldc_R8, default(Double));
1037                     break;
1038
1039                 case TypeCode.Decimal:
1040                     il.Emit(OpCodes.Ldc_I4_0);
1041                     il.Emit(OpCodes.Newobj, typeof(Decimal).GetConstructor(new Type[] { typeof(int) }));
1042                     break;
1043
1044                 default:
1045                     throw ContractUtils.Unreachable;
1046             }
1047         }
1048
1049         #endregion
1050     }
1051 }