/Microsoft.Scripting.Core/Compiler/ILGen.cs
C# | 1059 lines | 863 code | 127 blank | 69 comment | 199 complexity | 1ce3bfd5e8911226a3eb99a4180dc6d8 MD5 | raw file
- /* ****************************************************************************
- *
- * 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 Microsoft;
-
-
- #if CODEPLEX_40
- using ILGenerator = System.Linq.Expressions.Compiler.OffsetTrackingILGenerator;
- #else
- using ILGenerator = Microsoft.Linq.Expressions.Compiler.OffsetTrackingILGenerator;
- #endif
-
- using System.Collections.Generic;
- using System.Diagnostics;
- #if CODEPLEX_40
- using System.Dynamic.Utils;
- #else
- using Microsoft.Scripting.Utils;
- #endif
- using System.Reflection;
- using System.Reflection.Emit;
- using System.Runtime.CompilerServices;
- #if !CODEPLEX_40
- using Microsoft.Runtime.CompilerServices;
- #endif
-
-
- #if CODEPLEX_40
- namespace System.Linq.Expressions.Compiler {
- #else
- namespace Microsoft.Linq.Expressions.Compiler {
- #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);
- ContractUtils.Requires(ci != null, "type", Strings.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);
- 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) }));
- }
- type = TypeUtils.GetConstantType(type);
- 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 (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)) {
- 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) {
- 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 {
- if (isFromUnsigned) {
- switch (tc) {
- case TypeCode.SByte:
- case TypeCode.Byte:
- il.Emit(OpCodes.Conv_U1);
- break;
- case TypeCode.Int16:
- case TypeCode.UInt16:
- case TypeCode.Char:
- il.Emit(OpCodes.Conv_U2);
- break;
- case TypeCode.Int32:
- case TypeCode.UInt32:
- il.Emit(OpCodes.Conv_U4);
- break;
- case TypeCode.Int64:
- case TypeCode.UInt64:
- il.Emit(OpCodes.Conv_U8);
- break;
- default:
- throw Error.UnhandledConvert(typeTo);
- }
- } else {
- switch (tc) {
- case TypeCode.SByte:
- case TypeCode.Byte:
- il.Emit(OpCodes.Conv_I1);
- break;
- case TypeCode.Int16:
- case TypeCode.UInt16:
- case TypeCode.Char:
- il.Emit(OpCodes.Conv_I2);
- break;
- case TypeCode.Int32:
- case TypeCode.UInt32:
- il.Emit(OpCodes.Conv_I4);
- break;
- case TypeCode.Int64:
- case TypeCode.UInt64:
- 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");
- ContractUtils.Requires(count >= 0, "count", Strings.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");
- ContractUtils.Requires(arrayType.IsArray, "arrayType", Strings.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
- }
- }