System.Drawing: added email to icon and test file headers
[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
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                typeFrom == typeof(System.Enum) ||
616                typeFrom == typeof(System.ValueType) ||
617                TypeUtils.IsLegalExplicitVariantDelegateConversion(typeFrom, typeTo))
618             {
619                 il.EmitCastToType(typeFrom, typeTo);
620             } else if (isTypeFromNullable || isTypeToNullable) {
621                 il.EmitNullableConversion(typeFrom, typeTo, isChecked);
622             } else if (!(TypeUtils.IsConvertible(typeFrom) && TypeUtils.IsConvertible(typeTo)) // primitive runtime conversion
623                        &&
624                        (nnExprType.IsAssignableFrom(nnType) || // down cast
625                        nnType.IsAssignableFrom(nnExprType))) // up cast
626             {
627                 il.EmitCastToType(typeFrom, typeTo);
628             } else if (typeFrom.IsArray && typeTo.IsArray) {
629                 // See DevDiv Bugs #94657.
630                 il.EmitCastToType(typeFrom, typeTo);
631             } else {
632                 il.EmitNumericConversion(typeFrom, typeTo, isChecked);
633             }
634         }
635
636
637         private static void EmitCastToType(this ILGenerator il, Type typeFrom, Type typeTo) {
638             if (!typeFrom.IsValueType && typeTo.IsValueType) {
639                 il.Emit(OpCodes.Unbox_Any, typeTo);
640             } else if (typeFrom.IsValueType && !typeTo.IsValueType) {
641                 il.Emit(OpCodes.Box, typeFrom);
642                 if (typeTo != typeof(object)) {
643                     il.Emit(OpCodes.Castclass, typeTo);
644                 }
645             } else if (!typeFrom.IsValueType && !typeTo.IsValueType) {
646                 il.Emit(OpCodes.Castclass, typeTo);
647             } else {
648                 throw Error.InvalidCast(typeFrom, typeTo);
649             }
650         }
651
652
653         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
654         private static void EmitNumericConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
655             bool isFromUnsigned = TypeUtils.IsUnsigned(typeFrom);
656             bool isFromFloatingPoint = TypeUtils.IsFloatingPoint(typeFrom);
657             if (typeTo == typeof(Single)) {
658                 if (isFromUnsigned)
659                     il.Emit(OpCodes.Conv_R_Un);
660                 il.Emit(OpCodes.Conv_R4);
661             } else if (typeTo == typeof(Double)) {
662                 if (isFromUnsigned)
663                     il.Emit(OpCodes.Conv_R_Un);
664                 il.Emit(OpCodes.Conv_R8);
665             } else {
666                 TypeCode tc = Type.GetTypeCode(typeTo);
667                 if (isChecked) {
668                     // Overflow checking needs to know if the source value on the IL stack is unsigned or not.
669                     if (isFromUnsigned) {
670                         switch (tc) {
671                             case TypeCode.SByte:
672                                 il.Emit(OpCodes.Conv_Ovf_I1_Un);
673                                 break;
674                             case TypeCode.Int16:
675                                 il.Emit(OpCodes.Conv_Ovf_I2_Un);
676                                 break;
677                             case TypeCode.Int32:
678                                 il.Emit(OpCodes.Conv_Ovf_I4_Un);
679                                 break;
680                             case TypeCode.Int64:
681                                 il.Emit(OpCodes.Conv_Ovf_I8_Un);
682                                 break;
683                             case TypeCode.Byte:
684                                 il.Emit(OpCodes.Conv_Ovf_U1_Un);
685                                 break;
686                             case TypeCode.UInt16:
687                             case TypeCode.Char:
688                                 il.Emit(OpCodes.Conv_Ovf_U2_Un);
689                                 break;
690                             case TypeCode.UInt32:
691                                 il.Emit(OpCodes.Conv_Ovf_U4_Un);
692                                 break;
693                             case TypeCode.UInt64:
694                                 il.Emit(OpCodes.Conv_Ovf_U8_Un);
695                                 break;
696                             default:
697                                 throw Error.UnhandledConvert(typeTo);
698                         }
699                     } else {
700                         switch (tc) {
701                             case TypeCode.SByte:
702                                 il.Emit(OpCodes.Conv_Ovf_I1);
703                                 break;
704                             case TypeCode.Int16:
705                                 il.Emit(OpCodes.Conv_Ovf_I2);
706                                 break;
707                             case TypeCode.Int32:
708                                 il.Emit(OpCodes.Conv_Ovf_I4);
709                                 break;
710                             case TypeCode.Int64:
711                                 il.Emit(OpCodes.Conv_Ovf_I8);
712                                 break;
713                             case TypeCode.Byte:
714                                 il.Emit(OpCodes.Conv_Ovf_U1);
715                                 break;
716                             case TypeCode.UInt16:
717                             case TypeCode.Char:
718                                 il.Emit(OpCodes.Conv_Ovf_U2);
719                                 break;
720                             case TypeCode.UInt32:
721                                 il.Emit(OpCodes.Conv_Ovf_U4);
722                                 break;
723                             case TypeCode.UInt64:
724                                 il.Emit(OpCodes.Conv_Ovf_U8);
725                                 break;
726                             default:
727                                 throw Error.UnhandledConvert(typeTo);
728                         }
729                     }
730                 } else {
731                     switch (tc) {
732                         case TypeCode.SByte:
733                             il.Emit(OpCodes.Conv_I1);
734                             break;
735                         case TypeCode.Byte:
736                             il.Emit(OpCodes.Conv_U1);
737                             break;
738                         case TypeCode.Int16:
739                             il.Emit(OpCodes.Conv_I2);
740                             break;
741                         case TypeCode.UInt16:
742                         case TypeCode.Char:
743                             il.Emit(OpCodes.Conv_U2);
744                             break;
745                         case TypeCode.Int32:
746                             il.Emit(OpCodes.Conv_I4);
747                             break;
748                         case TypeCode.UInt32:
749                             il.Emit(OpCodes.Conv_U4);
750                             break;
751                         case TypeCode.Int64:
752                             if (isFromUnsigned) {
753                                 il.Emit(OpCodes.Conv_U8);
754                             } else {
755                                 il.Emit(OpCodes.Conv_I8);
756                             }
757                             break;
758                         case TypeCode.UInt64:
759                             if (isFromUnsigned || isFromFloatingPoint) {
760                                 il.Emit(OpCodes.Conv_U8);
761                             } else {
762                                 il.Emit(OpCodes.Conv_I8);
763                             }
764                             break;
765                         default:
766                             throw Error.UnhandledConvert(typeTo);
767                     }
768                 }
769             }
770         }
771
772         private static void EmitNullableToNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
773             Debug.Assert(TypeUtils.IsNullableType(typeFrom));
774             Debug.Assert(TypeUtils.IsNullableType(typeTo));
775             Label labIfNull = default(Label);
776             Label labEnd = default(Label);
777             LocalBuilder locFrom = null;
778             LocalBuilder locTo = null;
779             locFrom = il.DeclareLocal(typeFrom);
780             il.Emit(OpCodes.Stloc, locFrom);
781             locTo = il.DeclareLocal(typeTo);
782             // test for null
783             il.Emit(OpCodes.Ldloca, locFrom);
784             il.EmitHasValue(typeFrom);
785             labIfNull = il.DefineLabel();
786             il.Emit(OpCodes.Brfalse_S, labIfNull);
787             il.Emit(OpCodes.Ldloca, locFrom);
788             il.EmitGetValueOrDefault(typeFrom);
789             Type nnTypeFrom = TypeUtils.GetNonNullableType(typeFrom);
790             Type nnTypeTo = TypeUtils.GetNonNullableType(typeTo);
791             il.EmitConvertToType(nnTypeFrom, nnTypeTo, isChecked);
792             // construct result type
793             ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo });
794             il.Emit(OpCodes.Newobj, ci);
795             il.Emit(OpCodes.Stloc, locTo);
796             labEnd = il.DefineLabel();
797             il.Emit(OpCodes.Br_S, labEnd);
798             // if null then create a default one
799             il.MarkLabel(labIfNull);
800             il.Emit(OpCodes.Ldloca, locTo);
801             il.Emit(OpCodes.Initobj, typeTo);
802             il.MarkLabel(labEnd);
803             il.Emit(OpCodes.Ldloc, locTo);
804         }
805
806
807         private static void EmitNonNullableToNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
808             Debug.Assert(!TypeUtils.IsNullableType(typeFrom));
809             Debug.Assert(TypeUtils.IsNullableType(typeTo));
810             LocalBuilder locTo = null;
811             locTo = il.DeclareLocal(typeTo);
812             Type nnTypeTo = TypeUtils.GetNonNullableType(typeTo);
813             il.EmitConvertToType(typeFrom, nnTypeTo, isChecked);
814             ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo });
815             il.Emit(OpCodes.Newobj, ci);
816             il.Emit(OpCodes.Stloc, locTo);
817             il.Emit(OpCodes.Ldloc, locTo);
818         }
819
820
821         private static void EmitNullableToNonNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
822             Debug.Assert(TypeUtils.IsNullableType(typeFrom));
823             Debug.Assert(!TypeUtils.IsNullableType(typeTo));
824             if (typeTo.IsValueType)
825                 il.EmitNullableToNonNullableStructConversion(typeFrom, typeTo, isChecked);
826             else
827                 il.EmitNullableToReferenceConversion(typeFrom);
828         }
829
830
831         private static void EmitNullableToNonNullableStructConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
832             Debug.Assert(TypeUtils.IsNullableType(typeFrom));
833             Debug.Assert(!TypeUtils.IsNullableType(typeTo));
834             Debug.Assert(typeTo.IsValueType);
835             LocalBuilder locFrom = null;
836             locFrom = il.DeclareLocal(typeFrom);
837             il.Emit(OpCodes.Stloc, locFrom);
838             il.Emit(OpCodes.Ldloca, locFrom);
839             il.EmitGetValue(typeFrom);
840             Type nnTypeFrom = TypeUtils.GetNonNullableType(typeFrom);
841             il.EmitConvertToType(nnTypeFrom, typeTo, isChecked);
842         }
843
844
845         private static void EmitNullableToReferenceConversion(this ILGenerator il, Type typeFrom) {
846             Debug.Assert(TypeUtils.IsNullableType(typeFrom));
847             // We've got a conversion from nullable to Object, ValueType, Enum, etc.  Just box it so that
848             // we get the nullable semantics.  
849             il.Emit(OpCodes.Box, typeFrom);
850         }
851
852
853         private static void EmitNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
854             bool isTypeFromNullable = TypeUtils.IsNullableType(typeFrom);
855             bool isTypeToNullable = TypeUtils.IsNullableType(typeTo);
856             Debug.Assert(isTypeFromNullable || isTypeToNullable);
857             if (isTypeFromNullable && isTypeToNullable)
858                 il.EmitNullableToNullableConversion(typeFrom, typeTo, isChecked);
859             else if (isTypeFromNullable)
860                 il.EmitNullableToNonNullableConversion(typeFrom, typeTo, isChecked);
861             else
862                 il.EmitNonNullableToNullableConversion(typeFrom, typeTo, isChecked);
863         }
864
865
866         internal static void EmitHasValue(this ILGenerator il, Type nullableType) {
867             MethodInfo mi = nullableType.GetMethod("get_HasValue", BindingFlags.Instance | BindingFlags.Public);
868             Debug.Assert(nullableType.IsValueType);
869             il.Emit(OpCodes.Call, mi);
870         }
871
872
873         internal static void EmitGetValue(this ILGenerator il, Type nullableType) {
874             MethodInfo mi = nullableType.GetMethod("get_Value", BindingFlags.Instance | BindingFlags.Public);
875             Debug.Assert(nullableType.IsValueType);
876             il.Emit(OpCodes.Call, mi);
877         }
878
879
880         internal static void EmitGetValueOrDefault(this ILGenerator il, Type nullableType) {
881             MethodInfo mi = nullableType.GetMethod("GetValueOrDefault", System.Type.EmptyTypes);
882             Debug.Assert(nullableType.IsValueType);
883             il.Emit(OpCodes.Call, mi);
884         }
885
886         #endregion
887
888         #region Arrays
889
890         /// <summary>
891         /// Emits an array of constant values provided in the given list.
892         /// The array is strongly typed.
893         /// </summary>
894         internal static void EmitArray<T>(this ILGenerator il, IList<T> items) {
895             ContractUtils.RequiresNotNull(items, "items");
896
897             il.EmitInt(items.Count);
898             il.Emit(OpCodes.Newarr, typeof(T));
899             for (int i = 0; i < items.Count; i++) {
900                 il.Emit(OpCodes.Dup);
901                 il.EmitInt(i);
902                 il.EmitConstant(items[i], typeof(T));
903                 il.EmitStoreElement(typeof(T));
904             }
905         }
906
907         /// <summary>
908         /// Emits an array of values of count size.  The items are emitted via the callback
909         /// which is provided with the current item index to emit.
910         /// </summary>
911         internal static void EmitArray(this ILGenerator il, Type elementType, int count, Action<int> emit) {
912             ContractUtils.RequiresNotNull(elementType, "elementType");
913             ContractUtils.RequiresNotNull(emit, "emit");
914             if (count < 0) throw Error.CountCannotBeNegative();
915
916             il.EmitInt(count);
917             il.Emit(OpCodes.Newarr, elementType);
918             for (int i = 0; i < count; i++) {
919                 il.Emit(OpCodes.Dup);
920                 il.EmitInt(i);
921
922                 emit(i);
923
924                 il.EmitStoreElement(elementType);
925             }
926         }
927
928         /// <summary>
929         /// Emits an array construction code.  
930         /// The code assumes that bounds for all dimensions
931         /// are already emitted.
932         /// </summary>
933         internal static void EmitArray(this ILGenerator il, Type arrayType) {
934             ContractUtils.RequiresNotNull(arrayType, "arrayType");
935             if (!arrayType.IsArray) throw Error.ArrayTypeMustBeArray();
936
937             int rank = arrayType.GetArrayRank();
938             if (rank == 1) {
939                 il.Emit(OpCodes.Newarr, arrayType.GetElementType());
940             } else {
941                 Type[] types = new Type[rank];
942                 for (int i = 0; i < rank; i++) {
943                     types[i] = typeof(int);
944                 }
945                 il.EmitNew(arrayType, types);
946             }
947         }
948
949         #endregion
950
951         #region Support for emitting constants
952
953         internal static void EmitDecimal(this ILGenerator il, decimal value) {
954             if (Decimal.Truncate(value) == value) {
955                 if (Int32.MinValue <= value && value <= Int32.MaxValue) {
956                     int intValue = Decimal.ToInt32(value);
957                     il.EmitInt(intValue);
958                     il.EmitNew(typeof(Decimal).GetConstructor(new Type[] { typeof(int) }));
959                 } else if (Int64.MinValue <= value && value <= Int64.MaxValue) {
960                     long longValue = Decimal.ToInt64(value);
961                     il.EmitLong(longValue);
962                     il.EmitNew(typeof(Decimal).GetConstructor(new Type[] { typeof(long) }));
963                 } else {
964                     il.EmitDecimalBits(value);
965                 }
966             } else {
967                 il.EmitDecimalBits(value);
968             }
969         }
970
971         private static void EmitDecimalBits(this ILGenerator il, decimal value) {
972             int[] bits = Decimal.GetBits(value);
973             il.EmitInt(bits[0]);
974             il.EmitInt(bits[1]);
975             il.EmitInt(bits[2]);
976             il.EmitBoolean((bits[3] & 0x80000000) != 0);
977             il.EmitByte((byte)(bits[3] >> 16));
978             il.EmitNew(typeof(decimal).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int), typeof(bool), typeof(byte) }));
979         }
980
981         /// <summary>
982         /// Emits default(T)
983         /// Semantics match C# compiler behavior
984         /// </summary>
985         internal static void EmitDefault(this ILGenerator il, Type type) {
986             switch (Type.GetTypeCode(type)) {
987                 case TypeCode.Object:
988                 case TypeCode.DateTime:
989                     if (type.IsValueType) {
990                         // Type.GetTypeCode on an enum returns the underlying
991                         // integer TypeCode, so we won't get here.
992                         Debug.Assert(!type.IsEnum);
993
994                         // This is the IL for default(T) if T is a generic type
995                         // parameter, so it should work for any type. It's also
996                         // the standard pattern for structs.
997                         LocalBuilder lb = il.DeclareLocal(type);
998                         il.Emit(OpCodes.Ldloca, lb);
999                         il.Emit(OpCodes.Initobj, type);
1000                         il.Emit(OpCodes.Ldloc, lb);
1001                     } else {
1002                         il.Emit(OpCodes.Ldnull);
1003                     }
1004                     break;
1005
1006                 case TypeCode.Empty:
1007                 case TypeCode.String:
1008                 case TypeCode.DBNull:
1009                     il.Emit(OpCodes.Ldnull);
1010                     break;
1011
1012                 case TypeCode.Boolean:
1013                 case TypeCode.Char:
1014                 case TypeCode.SByte:
1015                 case TypeCode.Byte:
1016                 case TypeCode.Int16:
1017                 case TypeCode.UInt16:
1018                 case TypeCode.Int32:
1019                 case TypeCode.UInt32:
1020                     il.Emit(OpCodes.Ldc_I4_0);
1021                     break;
1022
1023                 case TypeCode.Int64:
1024                 case TypeCode.UInt64:
1025                     il.Emit(OpCodes.Ldc_I4_0);
1026                     il.Emit(OpCodes.Conv_I8);
1027                     break;
1028
1029                 case TypeCode.Single:
1030                     il.Emit(OpCodes.Ldc_R4, default(Single));
1031                     break;
1032
1033                 case TypeCode.Double:
1034                     il.Emit(OpCodes.Ldc_R8, default(Double));
1035                     break;
1036
1037                 case TypeCode.Decimal:
1038                     il.Emit(OpCodes.Ldc_I4_0);
1039                     il.Emit(OpCodes.Newobj, typeof(Decimal).GetConstructor(new Type[] { typeof(int) }));
1040                     break;
1041
1042                 default:
1043                     throw ContractUtils.Unreachable;
1044             }
1045         }
1046
1047         #endregion
1048     }
1049 }