ILGen.cs :  » Script » IronRuby » Microsoft » Scripting » Ast » Compiler » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Script » IronRuby 
IronRuby » Microsoft » Scripting » Ast » Compiler » ILGen.cs
/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, 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 Microsoft Public License.
 *
 * 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) ||
               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
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.