File: Microsoft\Scripting\Compiler\ILGen.cs
Project: ndp\fx\src\Core\System.Core.csproj (System.Core)
/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Apache License, Version 2.0, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Apache License, Version 2.0.
 *
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic.Utils;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
 
#if SILVERLIGHT
using System.Core;
#endif
 
#if CLR2
namespace Microsoft.Scripting.Ast.Compiler {
#else
namespace System.Linq.Expressions.Compiler {
#endif
#if CLR2 || SILVERLIGHT
    using ILGenerator = OffsetTrackingILGenerator;
#endif
 
    internal static class ILGen {
 
        internal static void Emit(this ILGenerator il, OpCode opcode, MethodBase methodBase) {
            Debug.Assert(methodBase is MethodInfo || methodBase is ConstructorInfo);
 
            if (methodBase.MemberType == MemberTypes.Constructor) {
                il.Emit(opcode, (ConstructorInfo)methodBase);
            } else {
                il.Emit(opcode, (MethodInfo)methodBase);
            }
        }
 
        #region Instruction helpers
 
        internal static void EmitLoadArg(this ILGenerator il, int index) {
            Debug.Assert(index >= 0);
 
            switch (index) {
                case 0:
                    il.Emit(OpCodes.Ldarg_0);
                    break;
                case 1:
                    il.Emit(OpCodes.Ldarg_1);
                    break;
                case 2:
                    il.Emit(OpCodes.Ldarg_2);
                    break;
                case 3:
                    il.Emit(OpCodes.Ldarg_3);
                    break;
                default:
                    if (index <= Byte.MaxValue) {
                        il.Emit(OpCodes.Ldarg_S, (byte)index);
                    } else {
                        il.Emit(OpCodes.Ldarg, index);
                    }
                    break;
            }
        }
 
        internal static void EmitLoadArgAddress(this ILGenerator il, int index) {
            Debug.Assert(index >= 0);
 
            if (index <= Byte.MaxValue) {
                il.Emit(OpCodes.Ldarga_S, (byte)index);
            } else {
                il.Emit(OpCodes.Ldarga, index);
            }
        }
 
        internal static void EmitStoreArg(this ILGenerator il, int index) {
            Debug.Assert(index >= 0);
 
            if (index <= Byte.MaxValue) {
                il.Emit(OpCodes.Starg_S, (byte)index);
            } else {
                il.Emit(OpCodes.Starg, index);
            }
        }
 
        /// <summary>
        /// Emits a Ldind* instruction for the appropriate type
        /// </summary>
        internal static void EmitLoadValueIndirect(this ILGenerator il, Type type) {
            ContractUtils.RequiresNotNull(type, "type");
 
            if (type.IsValueType) {
                if (type == typeof(int)) {
                    il.Emit(OpCodes.Ldind_I4);
                } else if (type == typeof(uint)) {
                    il.Emit(OpCodes.Ldind_U4);
                } else if (type == typeof(short)) {
                    il.Emit(OpCodes.Ldind_I2);
                } else if (type == typeof(ushort)) {
                    il.Emit(OpCodes.Ldind_U2);
                } else if (type == typeof(long) || type == typeof(ulong)) {
                    il.Emit(OpCodes.Ldind_I8);
                } else if (type == typeof(char)) {
                    il.Emit(OpCodes.Ldind_I2);
                } else if (type == typeof(bool)) {
                    il.Emit(OpCodes.Ldind_I1);
                } else if (type == typeof(float)) {
                    il.Emit(OpCodes.Ldind_R4);
                } else if (type == typeof(double)) {
                    il.Emit(OpCodes.Ldind_R8);
                } else {
                    il.Emit(OpCodes.Ldobj, type);
                }
            } else {
                il.Emit(OpCodes.Ldind_Ref);
            }
        }
 
 
        /// <summary>
        /// Emits a Stind* instruction for the appropriate type.
        /// </summary>
        internal static void EmitStoreValueIndirect(this ILGenerator il, Type type) {
            ContractUtils.RequiresNotNull(type, "type");
 
            if (type.IsValueType) {
                if (type == typeof(int)) {
                    il.Emit(OpCodes.Stind_I4);
                } else if (type == typeof(short)) {
                    il.Emit(OpCodes.Stind_I2);
                } else if (type == typeof(long) || type == typeof(ulong)) {
                    il.Emit(OpCodes.Stind_I8);
                } else if (type == typeof(char)) {
                    il.Emit(OpCodes.Stind_I2);
                } else if (type == typeof(bool)) {
                    il.Emit(OpCodes.Stind_I1);
                } else if (type == typeof(float)) {
                    il.Emit(OpCodes.Stind_R4);
                } else if (type == typeof(double)) {
                    il.Emit(OpCodes.Stind_R8);
                } else {
                    il.Emit(OpCodes.Stobj, type);
                }
            } else {
                il.Emit(OpCodes.Stind_Ref);
            }
        }
 
        // Emits the Ldelem* instruction for the appropriate type
 
        internal static void EmitLoadElement(this ILGenerator il, Type type) {
            ContractUtils.RequiresNotNull(type, "type");
 
            if (!type.IsValueType) {
                il.Emit(OpCodes.Ldelem_Ref);
            } else if (type.IsEnum) {
                il.Emit(OpCodes.Ldelem, type);
            } else {
                switch (Type.GetTypeCode(type)) {
                    case TypeCode.Boolean:
                    case TypeCode.SByte:
                        il.Emit(OpCodes.Ldelem_I1);
                        break;
                    case TypeCode.Byte:
                        il.Emit(OpCodes.Ldelem_U1);
                        break;
                    case TypeCode.Int16:
                        il.Emit(OpCodes.Ldelem_I2);
                        break;
                    case TypeCode.Char:
                    case TypeCode.UInt16:
                        il.Emit(OpCodes.Ldelem_U2);
                        break;
                    case TypeCode.Int32:
                        il.Emit(OpCodes.Ldelem_I4);
                        break;
                    case TypeCode.UInt32:
                        il.Emit(OpCodes.Ldelem_U4);
                        break;
                    case TypeCode.Int64:
                    case TypeCode.UInt64:
                        il.Emit(OpCodes.Ldelem_I8);
                        break;
                    case TypeCode.Single:
                        il.Emit(OpCodes.Ldelem_R4);
                        break;
                    case TypeCode.Double:
                        il.Emit(OpCodes.Ldelem_R8);
                        break;
                    default:
                        il.Emit(OpCodes.Ldelem, type);
                        break;
                }
            }
        }
 
        /// <summary>
        /// Emits a Stelem* instruction for the appropriate type.
        /// </summary>
        internal static void EmitStoreElement(this ILGenerator il, Type type) {
            ContractUtils.RequiresNotNull(type, "type");
 
            if (type.IsEnum) {
                il.Emit(OpCodes.Stelem, type);
                return;
            }
            switch (Type.GetTypeCode(type)) {
                case TypeCode.Boolean:
                case TypeCode.SByte:
                case TypeCode.Byte:
                    il.Emit(OpCodes.Stelem_I1);
                    break;
                case TypeCode.Char:
                case TypeCode.Int16:
                case TypeCode.UInt16:
                    il.Emit(OpCodes.Stelem_I2);
                    break;
                case TypeCode.Int32:
                case TypeCode.UInt32:
                    il.Emit(OpCodes.Stelem_I4);
                    break;
                case TypeCode.Int64:
                case TypeCode.UInt64:
                    il.Emit(OpCodes.Stelem_I8);
                    break;
                case TypeCode.Single:
                    il.Emit(OpCodes.Stelem_R4);
                    break;
                case TypeCode.Double:
                    il.Emit(OpCodes.Stelem_R8);
                    break;
                default:
                    if (type.IsValueType) {
                        il.Emit(OpCodes.Stelem, type);
                    } else {
                        il.Emit(OpCodes.Stelem_Ref);
                    }
                    break;
            }
        }
 
        internal static void EmitType(this ILGenerator il, Type type) {
            ContractUtils.RequiresNotNull(type, "type");
 
            il.Emit(OpCodes.Ldtoken, type);
            il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
        }
 
        #endregion
 
        #region Fields, properties and methods
 
        internal static void EmitFieldAddress(this ILGenerator il, FieldInfo fi) {
            ContractUtils.RequiresNotNull(fi, "fi");
 
            if (fi.IsStatic) {
                il.Emit(OpCodes.Ldsflda, fi);
            } else {
                il.Emit(OpCodes.Ldflda, fi);
            }
        }
 
        internal static void EmitFieldGet(this ILGenerator il, FieldInfo fi) {
            ContractUtils.RequiresNotNull(fi, "fi");
 
            if (fi.IsStatic) {
                il.Emit(OpCodes.Ldsfld, fi);
            } else {
                il.Emit(OpCodes.Ldfld, fi);
            }
        }
 
        internal static void EmitFieldSet(this ILGenerator il, FieldInfo fi) {
            ContractUtils.RequiresNotNull(fi, "fi");
 
            if (fi.IsStatic) {
                il.Emit(OpCodes.Stsfld, fi);
            } else {
                il.Emit(OpCodes.Stfld, fi);
            }
        }
 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
        internal static void EmitNew(this ILGenerator il, ConstructorInfo ci) {
            ContractUtils.RequiresNotNull(ci, "ci");
 
            if (ci.DeclaringType.ContainsGenericParameters) {
                throw Error.IllegalNewGenericParams(ci.DeclaringType);
            }
 
            il.Emit(OpCodes.Newobj, ci);
        }
 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
        internal static void EmitNew(this ILGenerator il, Type type, Type[] paramTypes) {
            ContractUtils.RequiresNotNull(type, "type");
            ContractUtils.RequiresNotNull(paramTypes, "paramTypes");
 
            ConstructorInfo ci = type.GetConstructor(paramTypes);
            if (ci == null) throw Error.TypeDoesNotHaveConstructorForTheSignature();
            il.EmitNew(ci);
        }
 
        #endregion
 
        #region Constants
 
        internal static void EmitNull(this ILGenerator il) {
            il.Emit(OpCodes.Ldnull);
        }
 
        internal static void EmitString(this ILGenerator il, string value) {
            ContractUtils.RequiresNotNull(value, "value");
            il.Emit(OpCodes.Ldstr, value);
        }
 
        internal static void EmitBoolean(this ILGenerator il, bool value) {
            if (value) {
                il.Emit(OpCodes.Ldc_I4_1);
            } else {
                il.Emit(OpCodes.Ldc_I4_0);
            }
        }
 
        internal static void EmitChar(this ILGenerator il, char value) {
            il.EmitInt(value);
            il.Emit(OpCodes.Conv_U2);
        }
 
        internal static void EmitByte(this ILGenerator il, byte value) {
            il.EmitInt(value);
            il.Emit(OpCodes.Conv_U1);
        }
 
        internal static void EmitSByte(this ILGenerator il, sbyte value) {
            il.EmitInt(value);
            il.Emit(OpCodes.Conv_I1);
        }
 
        internal static void EmitShort(this ILGenerator il, short value) {
            il.EmitInt(value);
            il.Emit(OpCodes.Conv_I2);
        }
 
        internal static void EmitUShort(this ILGenerator il, ushort value) {
            il.EmitInt(value);
            il.Emit(OpCodes.Conv_U2);
        }
 
        internal static void EmitInt(this ILGenerator il, int value) {
            OpCode c;
            switch (value) {
                case -1:
                    c = OpCodes.Ldc_I4_M1;
                    break;
                case 0:
                    c = OpCodes.Ldc_I4_0;
                    break;
                case 1:
                    c = OpCodes.Ldc_I4_1;
                    break;
                case 2:
                    c = OpCodes.Ldc_I4_2;
                    break;
                case 3:
                    c = OpCodes.Ldc_I4_3;
                    break;
                case 4:
                    c = OpCodes.Ldc_I4_4;
                    break;
                case 5:
                    c = OpCodes.Ldc_I4_5;
                    break;
                case 6:
                    c = OpCodes.Ldc_I4_6;
                    break;
                case 7:
                    c = OpCodes.Ldc_I4_7;
                    break;
                case 8:
                    c = OpCodes.Ldc_I4_8;
                    break;
                default:
                    if (value >= -128 && value <= 127) {
                        il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
                    } else {
                        il.Emit(OpCodes.Ldc_I4, value);
                    }
                    return;
            }
            il.Emit(c);
        }
 
        internal static void EmitUInt(this ILGenerator il, uint value) {
            il.EmitInt((int)value);
            il.Emit(OpCodes.Conv_U4);
        }
 
        internal static void EmitLong(this ILGenerator il, long value) {
            il.Emit(OpCodes.Ldc_I8, value);
 
            //
            // Now, emit convert to give the constant type information.
            //
            // Otherwise, it is treated as unsigned and overflow is not
            // detected if it's used in checked ops.
            //
            il.Emit(OpCodes.Conv_I8);
        }
 
        internal static void EmitULong(this ILGenerator il, ulong value) {
            il.Emit(OpCodes.Ldc_I8, (long)value);
            il.Emit(OpCodes.Conv_U8);
        }
 
        internal static void EmitDouble(this ILGenerator il, double value) {
            il.Emit(OpCodes.Ldc_R8, value);
        }
 
        internal static void EmitSingle(this ILGenerator il, float value) {
            il.Emit(OpCodes.Ldc_R4, value);
        }
 
        // matches TryEmitConstant
        internal static bool CanEmitConstant(object value, Type type) {
            if (value == null || CanEmitILConstant(type)) {
                return true;
            }
 
            Type t = value as Type;
            if (t != null && ShouldLdtoken(t)) {
                return true;
            }
 
            MethodBase mb = value as MethodBase;
            if (mb != null && ShouldLdtoken(mb)) {
                return true;
            }
 
            return false;
        }
 
        // matches TryEmitILConstant
        private static bool CanEmitILConstant(Type type) {
            switch (Type.GetTypeCode(type)) {
                case TypeCode.Boolean:
                case TypeCode.SByte:
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.Single:
                case TypeCode.Double:
                case TypeCode.Char:
                case TypeCode.Byte:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                case TypeCode.Decimal:
                case TypeCode.String:
                    return true;
            }
            return false;
        }
 
        internal static void EmitConstant(this ILGenerator il, object value) {
            Debug.Assert(value != null);
            EmitConstant(il, value, value.GetType());
        }
 
 
        //
        // Note: we support emitting more things as IL constants than
        // Linq does
        internal static void EmitConstant(this ILGenerator il, object value, Type type) {
            if (value == null) {
                // Smarter than the Linq implementation which uses the initobj
                // pattern for all value types (works, but requires a local and
                // more IL)
                il.EmitDefault(type);
                return;
            }
 
            // Handle the easy cases
            if (il.TryEmitILConstant(value, type)) {
                return;
            }
 
            // Check for a few more types that we support emitting as constants
            Type t = value as Type;
            if (t != null && ShouldLdtoken(t)) {
                il.EmitType(t);
                if (type != typeof(Type)) {
                    il.Emit(OpCodes.Castclass, type);
                }
                return;
            }
 
            MethodBase mb = value as MethodBase;
            if (mb != null && ShouldLdtoken(mb)) {
                il.Emit(OpCodes.Ldtoken, mb);
                Type dt = mb.DeclaringType;
                if (dt != null && dt.IsGenericType) {
                    il.Emit(OpCodes.Ldtoken, dt);
                    il.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetMethodFromHandle", new Type[] { typeof(RuntimeMethodHandle), typeof(RuntimeTypeHandle) }));
                } else {
                    il.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetMethodFromHandle", new Type[] { typeof(RuntimeMethodHandle) }));
                }
                if (type != typeof(MethodBase)) {
                    il.Emit(OpCodes.Castclass, type);
                }
                return;
            }
 
            throw ContractUtils.Unreachable;
        }
 
        internal static bool ShouldLdtoken(Type t) {
            return t is TypeBuilder || t.IsGenericParameter || t.IsVisible;
        }
 
        internal static bool ShouldLdtoken(MethodBase mb) {
            // Can't ldtoken on a DynamicMethod
            if (mb is DynamicMethod) {
                return false;
            }
 
            Type dt = mb.DeclaringType;
            return dt == null || ShouldLdtoken(dt);
        }
 
 
        private static bool TryEmitILConstant(this ILGenerator il, object value, Type type) {
            switch (Type.GetTypeCode(type)) {
                case TypeCode.Boolean:
                    il.EmitBoolean((bool)value);
                    return true;
                case TypeCode.SByte:
                    il.EmitSByte((sbyte)value);
                    return true;
                case TypeCode.Int16:
                    il.EmitShort((short)value);
                    return true;
                case TypeCode.Int32:
                    il.EmitInt((int)value);
                    return true;
                case TypeCode.Int64:
                    il.EmitLong((long)value);
                    return true;
                case TypeCode.Single:
                    il.EmitSingle((float)value);
                    return true;
                case TypeCode.Double:
                    il.EmitDouble((double)value);
                    return true;
                case TypeCode.Char:
                    il.EmitChar((char)value);
                    return true;
                case TypeCode.Byte:
                    il.EmitByte((byte)value);
                    return true;
                case TypeCode.UInt16:
                    il.EmitUShort((ushort)value);
                    return true;
                case TypeCode.UInt32:
                    il.EmitUInt((uint)value);
                    return true;
                case TypeCode.UInt64:
                    il.EmitULong((ulong)value);
                    return true;
                case TypeCode.Decimal:
                    il.EmitDecimal((decimal)value);
                    return true;
                case TypeCode.String:
                    il.EmitString((string)value);
                    return true;
                default:
                    return false;
            }
        }
 
        #endregion
 
        #region Linq Conversions
 
        internal static void EmitConvertToType(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
            if (TypeUtils.AreEquivalent(typeFrom, typeTo)) {
                return;
            }
 
            if (typeFrom == typeof(void) || typeTo == typeof(void)) {
                throw ContractUtils.Unreachable;
            }
 
            bool isTypeFromNullable = TypeUtils.IsNullableType(typeFrom);
            bool isTypeToNullable = TypeUtils.IsNullableType(typeTo);
 
            Type nnExprType = TypeUtils.GetNonNullableType(typeFrom);
            Type nnType = TypeUtils.GetNonNullableType(typeTo);
 
            if (typeFrom.IsInterface || // interface cast
               typeTo.IsInterface ||
               typeFrom == typeof(object) || // boxing cast
               typeTo == typeof(object) ||
               typeFrom == typeof(System.Enum) ||
               typeFrom == typeof(System.ValueType) ||
               TypeUtils.IsLegalExplicitVariantDelegateConversion(typeFrom, typeTo))
            {
                il.EmitCastToType(typeFrom, typeTo);
            } else if (isTypeFromNullable || isTypeToNullable) {
                il.EmitNullableConversion(typeFrom, typeTo, isChecked);
            } else if (!(TypeUtils.IsConvertible(typeFrom) && TypeUtils.IsConvertible(typeTo)) // primitive runtime conversion
                       &&
                       (nnExprType.IsAssignableFrom(nnType) || // down cast
                       nnType.IsAssignableFrom(nnExprType))) // up cast
            {
                il.EmitCastToType(typeFrom, typeTo);
            } else if (typeFrom.IsArray && typeTo.IsArray) {
                // See DevDiv Bugs #94657.
                il.EmitCastToType(typeFrom, typeTo);
            } else {
                il.EmitNumericConversion(typeFrom, typeTo, isChecked);
            }
        }
 
 
        private static void EmitCastToType(this ILGenerator il, Type typeFrom, Type typeTo) {
            if (!typeFrom.IsValueType && typeTo.IsValueType) {
                il.Emit(OpCodes.Unbox_Any, typeTo);
            } else if (typeFrom.IsValueType && !typeTo.IsValueType) {
                il.Emit(OpCodes.Box, typeFrom);
                if (typeTo != typeof(object)) {
                    il.Emit(OpCodes.Castclass, typeTo);
                }
            } else if (!typeFrom.IsValueType && !typeTo.IsValueType) {
                il.Emit(OpCodes.Castclass, typeTo);
            } else {
                throw Error.InvalidCast(typeFrom, typeTo);
            }
        }
 
 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        private static void EmitNumericConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
            bool isFromUnsigned = TypeUtils.IsUnsigned(typeFrom);
            bool isFromFloatingPoint = TypeUtils.IsFloatingPoint(typeFrom);
            if (typeTo == typeof(Single)) {
                if (isFromUnsigned)
                    il.Emit(OpCodes.Conv_R_Un);
                il.Emit(OpCodes.Conv_R4);
            } else if (typeTo == typeof(Double)) {
                if (isFromUnsigned)
                    il.Emit(OpCodes.Conv_R_Un);
                il.Emit(OpCodes.Conv_R8);
            } else {
                TypeCode tc = Type.GetTypeCode(typeTo);
                if (isChecked) {
                    // Overflow checking needs to know if the source value on the IL stack is unsigned or not.
                    if (isFromUnsigned) {
                        switch (tc) {
                            case TypeCode.SByte:
                                il.Emit(OpCodes.Conv_Ovf_I1_Un);
                                break;
                            case TypeCode.Int16:
                                il.Emit(OpCodes.Conv_Ovf_I2_Un);
                                break;
                            case TypeCode.Int32:
                                il.Emit(OpCodes.Conv_Ovf_I4_Un);
                                break;
                            case TypeCode.Int64:
                                il.Emit(OpCodes.Conv_Ovf_I8_Un);
                                break;
                            case TypeCode.Byte:
                                il.Emit(OpCodes.Conv_Ovf_U1_Un);
                                break;
                            case TypeCode.UInt16:
                            case TypeCode.Char:
                                il.Emit(OpCodes.Conv_Ovf_U2_Un);
                                break;
                            case TypeCode.UInt32:
                                il.Emit(OpCodes.Conv_Ovf_U4_Un);
                                break;
                            case TypeCode.UInt64:
                                il.Emit(OpCodes.Conv_Ovf_U8_Un);
                                break;
                            default:
                                throw Error.UnhandledConvert(typeTo);
                        }
                    } else {
                        switch (tc) {
                            case TypeCode.SByte:
                                il.Emit(OpCodes.Conv_Ovf_I1);
                                break;
                            case TypeCode.Int16:
                                il.Emit(OpCodes.Conv_Ovf_I2);
                                break;
                            case TypeCode.Int32:
                                il.Emit(OpCodes.Conv_Ovf_I4);
                                break;
                            case TypeCode.Int64:
                                il.Emit(OpCodes.Conv_Ovf_I8);
                                break;
                            case TypeCode.Byte:
                                il.Emit(OpCodes.Conv_Ovf_U1);
                                break;
                            case TypeCode.UInt16:
                            case TypeCode.Char:
                                il.Emit(OpCodes.Conv_Ovf_U2);
                                break;
                            case TypeCode.UInt32:
                                il.Emit(OpCodes.Conv_Ovf_U4);
                                break;
                            case TypeCode.UInt64:
                                il.Emit(OpCodes.Conv_Ovf_U8);
                                break;
                            default:
                                throw Error.UnhandledConvert(typeTo);
                        }
                    }
                } else {
                    switch (tc) {
                        case TypeCode.SByte:
                            il.Emit(OpCodes.Conv_I1);
                            break;
                        case TypeCode.Byte:
                            il.Emit(OpCodes.Conv_U1);
                            break;
                        case TypeCode.Int16:
                            il.Emit(OpCodes.Conv_I2);
                            break;
                        case TypeCode.UInt16:
                        case TypeCode.Char:
                            il.Emit(OpCodes.Conv_U2);
                            break;
                        case TypeCode.Int32:
                            il.Emit(OpCodes.Conv_I4);
                            break;
                        case TypeCode.UInt32:
                            il.Emit(OpCodes.Conv_U4);
                            break;
                        case TypeCode.Int64:
                            if (isFromUnsigned) {
                                il.Emit(OpCodes.Conv_U8);
                            } else {
                                il.Emit(OpCodes.Conv_I8);
                            }
                            break;
                        case TypeCode.UInt64:
                            if (isFromUnsigned || isFromFloatingPoint) {
                                il.Emit(OpCodes.Conv_U8);
                            } else {
                                il.Emit(OpCodes.Conv_I8);
                            }
                            break;
                        default:
                            throw Error.UnhandledConvert(typeTo);
                    }
                }
            }
        }
 
        private static void EmitNullableToNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
            Debug.Assert(TypeUtils.IsNullableType(typeFrom));
            Debug.Assert(TypeUtils.IsNullableType(typeTo));
            Label labIfNull = default(Label);
            Label labEnd = default(Label);
            LocalBuilder locFrom = null;
            LocalBuilder locTo = null;
            locFrom = il.DeclareLocal(typeFrom);
            il.Emit(OpCodes.Stloc, locFrom);
            locTo = il.DeclareLocal(typeTo);
            // test for null
            il.Emit(OpCodes.Ldloca, locFrom);
            il.EmitHasValue(typeFrom);
            labIfNull = il.DefineLabel();
            il.Emit(OpCodes.Brfalse_S, labIfNull);
            il.Emit(OpCodes.Ldloca, locFrom);
            il.EmitGetValueOrDefault(typeFrom);
            Type nnTypeFrom = TypeUtils.GetNonNullableType(typeFrom);
            Type nnTypeTo = TypeUtils.GetNonNullableType(typeTo);
            il.EmitConvertToType(nnTypeFrom, nnTypeTo, isChecked);
            // construct result type
            ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo });
            il.Emit(OpCodes.Newobj, ci);
            il.Emit(OpCodes.Stloc, locTo);
            labEnd = il.DefineLabel();
            il.Emit(OpCodes.Br_S, labEnd);
            // if null then create a default one
            il.MarkLabel(labIfNull);
            il.Emit(OpCodes.Ldloca, locTo);
            il.Emit(OpCodes.Initobj, typeTo);
            il.MarkLabel(labEnd);
            il.Emit(OpCodes.Ldloc, locTo);
        }
 
 
        private static void EmitNonNullableToNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
            Debug.Assert(!TypeUtils.IsNullableType(typeFrom));
            Debug.Assert(TypeUtils.IsNullableType(typeTo));
            LocalBuilder locTo = null;
            locTo = il.DeclareLocal(typeTo);
            Type nnTypeTo = TypeUtils.GetNonNullableType(typeTo);
            il.EmitConvertToType(typeFrom, nnTypeTo, isChecked);
            ConstructorInfo ci = typeTo.GetConstructor(new Type[] { nnTypeTo });
            il.Emit(OpCodes.Newobj, ci);
            il.Emit(OpCodes.Stloc, locTo);
            il.Emit(OpCodes.Ldloc, locTo);
        }
 
 
        private static void EmitNullableToNonNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
            Debug.Assert(TypeUtils.IsNullableType(typeFrom));
            Debug.Assert(!TypeUtils.IsNullableType(typeTo));
            if (typeTo.IsValueType)
                il.EmitNullableToNonNullableStructConversion(typeFrom, typeTo, isChecked);
            else
                il.EmitNullableToReferenceConversion(typeFrom);
        }
 
 
        private static void EmitNullableToNonNullableStructConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
            Debug.Assert(TypeUtils.IsNullableType(typeFrom));
            Debug.Assert(!TypeUtils.IsNullableType(typeTo));
            Debug.Assert(typeTo.IsValueType);
            LocalBuilder locFrom = null;
            locFrom = il.DeclareLocal(typeFrom);
            il.Emit(OpCodes.Stloc, locFrom);
            il.Emit(OpCodes.Ldloca, locFrom);
            il.EmitGetValue(typeFrom);
            Type nnTypeFrom = TypeUtils.GetNonNullableType(typeFrom);
            il.EmitConvertToType(nnTypeFrom, typeTo, isChecked);
        }
 
 
        private static void EmitNullableToReferenceConversion(this ILGenerator il, Type typeFrom) {
            Debug.Assert(TypeUtils.IsNullableType(typeFrom));
            // We've got a conversion from nullable to Object, ValueType, Enum, etc.  Just box it so that
            // we get the nullable semantics.  
            il.Emit(OpCodes.Box, typeFrom);
        }
 
 
        private static void EmitNullableConversion(this ILGenerator il, Type typeFrom, Type typeTo, bool isChecked) {
            bool isTypeFromNullable = TypeUtils.IsNullableType(typeFrom);
            bool isTypeToNullable = TypeUtils.IsNullableType(typeTo);
            Debug.Assert(isTypeFromNullable || isTypeToNullable);
            if (isTypeFromNullable && isTypeToNullable)
                il.EmitNullableToNullableConversion(typeFrom, typeTo, isChecked);
            else if (isTypeFromNullable)
                il.EmitNullableToNonNullableConversion(typeFrom, typeTo, isChecked);
            else
                il.EmitNonNullableToNullableConversion(typeFrom, typeTo, isChecked);
        }
 
 
        internal static void EmitHasValue(this ILGenerator il, Type nullableType) {
            MethodInfo mi = nullableType.GetMethod("get_HasValue", BindingFlags.Instance | BindingFlags.Public);
            Debug.Assert(nullableType.IsValueType);
            il.Emit(OpCodes.Call, mi);
        }
 
 
        internal static void EmitGetValue(this ILGenerator il, Type nullableType) {
            MethodInfo mi = nullableType.GetMethod("get_Value", BindingFlags.Instance | BindingFlags.Public);
            Debug.Assert(nullableType.IsValueType);
            il.Emit(OpCodes.Call, mi);
        }
 
 
        internal static void EmitGetValueOrDefault(this ILGenerator il, Type nullableType) {
            MethodInfo mi = nullableType.GetMethod("GetValueOrDefault", System.Type.EmptyTypes);
            Debug.Assert(nullableType.IsValueType);
            il.Emit(OpCodes.Call, mi);
        }
 
        #endregion
 
        #region Arrays
 
        /// <summary>
        /// Emits an array of constant values provided in the given list.
        /// The array is strongly typed.
        /// </summary>
        internal static void EmitArray<T>(this ILGenerator il, IList<T> items) {
            ContractUtils.RequiresNotNull(items, "items");
 
            il.EmitInt(items.Count);
            il.Emit(OpCodes.Newarr, typeof(T));
            for (int i = 0; i < items.Count; i++) {
                il.Emit(OpCodes.Dup);
                il.EmitInt(i);
                il.EmitConstant(items[i], typeof(T));
                il.EmitStoreElement(typeof(T));
            }
        }
 
        /// <summary>
        /// Emits an array of values of count size.  The items are emitted via the callback
        /// which is provided with the current item index to emit.
        /// </summary>
        internal static void EmitArray(this ILGenerator il, Type elementType, int count, Action<int> emit) {
            ContractUtils.RequiresNotNull(elementType, "elementType");
            ContractUtils.RequiresNotNull(emit, "emit");
            if (count < 0) throw Error.CountCannotBeNegative();
 
            il.EmitInt(count);
            il.Emit(OpCodes.Newarr, elementType);
            for (int i = 0; i < count; i++) {
                il.Emit(OpCodes.Dup);
                il.EmitInt(i);
 
                emit(i);
 
                il.EmitStoreElement(elementType);
            }
        }
 
        /// <summary>
        /// Emits an array construction code.  
        /// The code assumes that bounds for all dimensions
        /// are already emitted.
        /// </summary>
        internal static void EmitArray(this ILGenerator il, Type arrayType) {
            ContractUtils.RequiresNotNull(arrayType, "arrayType");
            if (!arrayType.IsArray) throw Error.ArrayTypeMustBeArray();
 
            int rank = arrayType.GetArrayRank();
            if (rank == 1) {
                il.Emit(OpCodes.Newarr, arrayType.GetElementType());
            } else {
                Type[] types = new Type[rank];
                for (int i = 0; i < rank; i++) {
                    types[i] = typeof(int);
                }
                il.EmitNew(arrayType, types);
            }
        }
 
        #endregion
 
        #region Support for emitting constants
 
        internal static void EmitDecimal(this ILGenerator il, decimal value) {
            if (Decimal.Truncate(value) == value) {
                if (Int32.MinValue <= value && value <= Int32.MaxValue) {
                    int intValue = Decimal.ToInt32(value);
                    il.EmitInt(intValue);
                    il.EmitNew(typeof(Decimal).GetConstructor(new Type[] { typeof(int) }));
                } else if (Int64.MinValue <= value && value <= Int64.MaxValue) {
                    long longValue = Decimal.ToInt64(value);
                    il.EmitLong(longValue);
                    il.EmitNew(typeof(Decimal).GetConstructor(new Type[] { typeof(long) }));
                } else {
                    il.EmitDecimalBits(value);
                }
            } else {
                il.EmitDecimalBits(value);
            }
        }
 
        private static void EmitDecimalBits(this ILGenerator il, decimal value) {
            int[] bits = Decimal.GetBits(value);
            il.EmitInt(bits[0]);
            il.EmitInt(bits[1]);
            il.EmitInt(bits[2]);
            il.EmitBoolean((bits[3] & 0x80000000) != 0);
            il.EmitByte((byte)(bits[3] >> 16));
            il.EmitNew(typeof(decimal).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int), typeof(bool), typeof(byte) }));
        }
 
        /// <summary>
        /// Emits default(T)
        /// Semantics match C# compiler behavior
        /// </summary>
        internal static void EmitDefault(this ILGenerator il, Type type) {
            switch (Type.GetTypeCode(type)) {
                case TypeCode.Object:
                case TypeCode.DateTime:
                    if (type.IsValueType) {
                        // Type.GetTypeCode on an enum returns the underlying
                        // integer TypeCode, so we won't get here.
                        Debug.Assert(!type.IsEnum);
 
                        // This is the IL for default(T) if T is a generic type
                        // parameter, so it should work for any type. It's also
                        // the standard pattern for structs.
                        LocalBuilder lb = il.DeclareLocal(type);
                        il.Emit(OpCodes.Ldloca, lb);
                        il.Emit(OpCodes.Initobj, type);
                        il.Emit(OpCodes.Ldloc, lb);
                    } else {
                        il.Emit(OpCodes.Ldnull);
                    }
                    break;
 
                case TypeCode.Empty:
                case TypeCode.String:
                case TypeCode.DBNull:
                    il.Emit(OpCodes.Ldnull);
                    break;
 
                case TypeCode.Boolean:
                case TypeCode.Char:
                case TypeCode.SByte:
                case TypeCode.Byte:
                case TypeCode.Int16:
                case TypeCode.UInt16:
                case TypeCode.Int32:
                case TypeCode.UInt32:
                    il.Emit(OpCodes.Ldc_I4_0);
                    break;
 
                case TypeCode.Int64:
                case TypeCode.UInt64:
                    il.Emit(OpCodes.Ldc_I4_0);
                    il.Emit(OpCodes.Conv_I8);
                    break;
 
                case TypeCode.Single:
                    il.Emit(OpCodes.Ldc_R4, default(Single));
                    break;
 
                case TypeCode.Double:
                    il.Emit(OpCodes.Ldc_R8, default(Double));
                    break;
 
                case TypeCode.Decimal:
                    il.Emit(OpCodes.Ldc_I4_0);
                    il.Emit(OpCodes.Newobj, typeof(Decimal).GetConstructor(new Type[] { typeof(int) }));
                    break;
 
                default:
                    throw ContractUtils.Unreachable;
            }
        }
 
        #endregion
    }
}