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