From 91d9dcc3d31f9f08b8a1ab6aa5eca87b94e14dd3 Mon Sep 17 00:00:00 2001 From: Hugh Bellamy Date: Fri, 25 Nov 2016 17:00:12 +0000 Subject: [PATCH 1/2] Implement IntPtr/UIntPtr enum casts in Linq expressions --- .../src/System/Dynamic/Utils/TypeUtils.cs | 2 +- .../System/Linq/Expressions/Compiler/ILGen.cs | 65 ++++++++++- .../Interpreter/EqualInstruction.cs | 101 +++++++++++++++++- .../Expressions/Interpreter/TypeOperations.cs | 14 ++- .../tests/Cast/CastTests.cs | 2 + .../tests/HelperTypes.cs | 82 ++++++++++++-- 6 files changed, 250 insertions(+), 16 deletions(-) diff --git a/src/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs b/src/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs index c6291d41ebda..082134ea24c6 100644 --- a/src/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs +++ b/src/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs @@ -403,7 +403,7 @@ public static bool IsConvertible(this Type type) case TypeCode.Char: return true; default: - return false; + return type == typeof(IntPtr) || type == typeof(UIntPtr); } } diff --git a/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs b/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs index 19b2f96bbaf5..a8cb02369cc0 100644 --- a/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs +++ b/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs @@ -867,6 +867,21 @@ private static void EmitNumericConversion(this ILGenerator il, Type typeFrom, Ty il.Emit(OpCodes.Conv_Ovf_U8_Un); break; default: + if (typeTo.IsEnum) + { + Type underlyingType = Enum.GetUnderlyingType(typeTo); + if (underlyingType == typeof(IntPtr)) + { + il.Emit(OpCodes.Conv_Ovf_I_Un); + break; + } + else + { + Debug.Assert(underlyingType == typeof(UIntPtr)); + il.Emit(OpCodes.Conv_Ovf_U_Un); + break; + } + } throw Error.UnhandledConvert(typeTo); } } @@ -900,6 +915,21 @@ private static void EmitNumericConversion(this ILGenerator il, Type typeFrom, Ty il.Emit(OpCodes.Conv_Ovf_U8); break; default: + if (typeTo.IsEnum) + { + Type underlyingType = Enum.GetUnderlyingType(typeTo); + if (underlyingType == typeof(IntPtr)) + { + il.Emit(OpCodes.Conv_Ovf_I); + break; + } + else + { + Debug.Assert(underlyingType == typeof(UIntPtr)); + il.Emit(OpCodes.Conv_Ovf_U); + break; + } + } throw Error.UnhandledConvert(typeTo); } } @@ -952,6 +982,21 @@ private static void EmitNumericConversion(this ILGenerator il, Type typeFrom, Ty il.Emit(OpCodes.Cgt_Un); break; default: + if (typeTo.IsEnum) + { + Type underlyingType = Enum.GetUnderlyingType(typeTo); + if (underlyingType == typeof(IntPtr)) + { + il.Emit(OpCodes.Conv_I); + break; + } + else + { + Debug.Assert(underlyingType == typeof(UIntPtr)); + il.Emit(OpCodes.Conv_U); + break; + } + } throw Error.UnhandledConvert(typeTo); } } @@ -1195,9 +1240,23 @@ internal static void EmitDefault(this ILGenerator il, Type type) case TypeCode.DateTime: if (type.GetTypeInfo().IsValueType) { - // Type.GetTypeCode on an enum returns the underlying - // integer TypeCode, so we won't get here. - Debug.Assert(!type.GetTypeInfo().IsEnum); + if (type.GetTypeInfo().IsEnum) + { + Type underlyingType = Enum.GetUnderlyingType(type); + if (underlyingType == typeof(IntPtr)) + { + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Conv_I); + break; + } + else + { + Debug.Assert(underlyingType == typeof(UIntPtr)); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Conv_U); + break; + } + } // This is the IL for default(T) if T is a generic type // parameter, so it should work for any type. It's also diff --git a/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/EqualInstruction.cs b/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/EqualInstruction.cs index c71e20679dcb..dbbce841b303 100644 --- a/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/EqualInstruction.cs +++ b/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/EqualInstruction.cs @@ -10,8 +10,8 @@ namespace System.Linq.Expressions.Interpreter internal abstract class EqualInstruction : Instruction { // Perf: EqualityComparer but is 3/2 to 2 times slower. - private static Instruction s_reference, s_boolean, s_SByte, s_int16, s_char, s_int32, s_int64, s_byte, s_UInt16, s_UInt32, s_UInt64, s_single, s_double; - private static Instruction s_referenceLiftedToNull, s_booleanLiftedToNull, s_SByteLiftedToNull, s_int16LiftedToNull, s_charLiftedToNull, s_int32LiftedToNull, s_int64LiftedToNull, s_byteLiftedToNull, s_UInt16LiftedToNull, s_UInt32LiftedToNull, s_UInt64LiftedToNull, s_singleLiftedToNull, s_doubleLiftedToNull; + private static Instruction s_reference, s_boolean, s_SByte, s_int16, s_char, s_int32, s_int64, s_byte, s_UInt16, s_UInt32, s_UInt64, s_single, s_double, s_intPtr, s_uintPtr; + private static Instruction s_referenceLiftedToNull, s_booleanLiftedToNull, s_SByteLiftedToNull, s_int16LiftedToNull, s_charLiftedToNull, s_int32LiftedToNull, s_int64LiftedToNull, s_byteLiftedToNull, s_UInt16LiftedToNull, s_UInt32LiftedToNull, s_UInt64LiftedToNull, s_singleLiftedToNull, s_doubleLiftedToNull, s_intPtrLiftedToNull, s_uintPtrLiftedToNull; public override int ConsumedStack => 2; public override int ProducedStack => 1; @@ -283,6 +283,50 @@ public override int Run(InterpretedFrame frame) } } + private sealed class EqualIntPtr : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object right = frame.Pop(); + object left = frame.Pop(); + if (left == null) + { + frame.Push(right == null); + } + else if (right == null) + { + frame.Push(false); + } + else + { + frame.Push((IntPtr)left == (IntPtr)right); + } + return +1; + } + } + + private sealed class EqualUIntPtr : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object right = frame.Pop(); + object left = frame.Pop(); + if (left == null) + { + frame.Push(right == null); + } + else if (right == null) + { + frame.Push(false); + } + else + { + frame.Push((UIntPtr)left == (UIntPtr)right); + } + return +1; + } + } + private sealed class EqualReference : EqualInstruction { public override int Run(InterpretedFrame frame) @@ -508,6 +552,41 @@ public override int Run(InterpretedFrame frame) } } + private sealed class EqualIntPtrLiftedToNull : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object right = frame.Pop(); + object left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((IntPtr)left == (IntPtr)right); + } + return +1; + } + } + + private sealed class EqualUIntPtrLiftedToNull : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object right = frame.Pop(); + object left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((UIntPtr)left == (UIntPtr)right); + } + return +1; + } + } private sealed class EqualReferenceLiftedToNull : EqualInstruction { @@ -559,6 +638,15 @@ public static Instruction Create(Type type, bool liftedToNull) { return s_referenceLiftedToNull ?? (s_referenceLiftedToNull = new EqualReferenceLiftedToNull()); } + else if (underlyingType == typeof(IntPtr)) + { + return s_intPtrLiftedToNull ?? (s_intPtrLiftedToNull = new EqualIntPtrLiftedToNull()); + } + else if (underlyingType == typeof(UIntPtr)) + { + return s_uintPtrLiftedToNull ?? (s_uintPtrLiftedToNull = new EqualUIntPtrLiftedToNull()); + } + // TODO: Nullable throw Error.ExpressionNotSupportedForNullableType("Equal", type); @@ -591,6 +679,15 @@ public static Instruction Create(Type type, bool liftedToNull) { return s_reference ?? (s_reference = new EqualReference()); } + else if (underlyingType == typeof(IntPtr)) + { + return s_intPtr ?? (s_intPtr = new EqualIntPtr()); + } + else if(underlyingType == typeof(UIntPtr)) + { + return s_uintPtr ?? (s_uintPtr = new EqualUIntPtr()); + } + // TODO: Nullable throw Error.ExpressionNotSupportedForNullableType("Equal", type); diff --git a/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/TypeOperations.cs b/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/TypeOperations.cs index dd5fe4b3d7d4..2c307ecedd00 100644 --- a/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/TypeOperations.cs +++ b/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/TypeOperations.cs @@ -493,12 +493,22 @@ public override int Run(InterpretedFrame frame) // Disallowed in C#, but allowed in CIL frame.Push(Enum.ToObject(_t, (char)from)); } + else if (underlying == typeof(bool)) + { + // Disallowed in C#, but allowed in CIL + frame.Push(Enum.ToObject(_t, (bool)from)); + } + else if (underlying == typeof(IntPtr)) + { + // Disallowed in C#, but allowed in CIL + frame.Push(Enum.ToObject(_t, ((IntPtr)from).ToInt64())); + } else { // Only remaining possible type. // Disallowed in C#, but allowed in CIL - Debug.Assert(underlying == typeof(bool)); - frame.Push(Enum.ToObject(_t, (bool)from)); + Debug.Assert(underlying == typeof(UIntPtr)); + frame.Push(Enum.ToObject(_t, ((UIntPtr)from).ToUInt64())); } return 1; diff --git a/src/System.Linq.Expressions/tests/Cast/CastTests.cs b/src/System.Linq.Expressions/tests/Cast/CastTests.cs index 9273ba72f1ee..8afff555a8c9 100644 --- a/src/System.Linq.Expressions/tests/Cast/CastTests.cs +++ b/src/System.Linq.Expressions/tests/Cast/CastTests.cs @@ -2376,6 +2376,8 @@ public static IEnumerable EnumerableTypes() yield return typeof(UInt64Enum); yield return NonCSharpTypes.CharEnumType; yield return NonCSharpTypes.BoolEnumType; + yield return NonCSharpTypes.IntPtrEnumType; + yield return NonCSharpTypes.UIntPtrEnumType; } public static IEnumerable EnumerableTypeArgs() => EnumerableTypes().Select(t => new object[] {t}); diff --git a/src/System.Linq.Expressions/tests/HelperTypes.cs b/src/System.Linq.Expressions/tests/HelperTypes.cs index 05d860a603c6..637890d65ef5 100644 --- a/src/System.Linq.Expressions/tests/HelperTypes.cs +++ b/src/System.Linq.Expressions/tests/HelperTypes.cs @@ -338,19 +338,23 @@ IEnumerator IEnumerable.GetEnumerator() } } - public enum ByteEnum : byte { A = Byte.MaxValue } - public enum SByteEnum : sbyte { A = SByte.MaxValue } - public enum Int16Enum : short { A = Int16.MaxValue } - public enum UInt16Enum : ushort { A = UInt16.MaxValue } - public enum Int32Enum : int { A = Int32.MaxValue } - public enum UInt32Enum : uint { A = UInt32.MaxValue } - public enum Int64Enum : long { A = Int64.MaxValue } - public enum UInt64Enum : ulong { A = UInt64.MaxValue } + public enum ByteEnum : byte { A = byte.MaxValue } + public enum SByteEnum : sbyte { A = sbyte.MaxValue } + public enum Int16Enum : short { A = short.MaxValue } + public enum UInt16Enum : ushort { A = ushort.MaxValue } + public enum Int32Enum : int { A = int.MaxValue } + public enum UInt32Enum : uint { A = uint.MaxValue } + public enum Int64Enum : long { A = long.MaxValue } + public enum UInt64Enum : ulong { A = ulong.MaxValue } public static class NonCSharpTypes { private static Type _charEnumType; private static Type _boolEnumType; + private static Type _floatEnumType; + private static Type _doubleEnumType; + private static Type _intPtrEnumType; + private static Type _uintPtrEnumType; private static ModuleBuilder GetModuleBuilder() { @@ -391,6 +395,68 @@ public static Type BoolEnumType return _boolEnumType; } } + + public static Type FloatEnumType + { + get + { + if (_floatEnumType == null) + { + EnumBuilder eb = GetModuleBuilder().DefineEnum("FloatEnumType", TypeAttributes.Public, typeof(float)); + eb.DefineLiteral("A", 1.0f); + eb.DefineLiteral("B", 2.0f); + eb.DefineLiteral("C", 3.0f); + _floatEnumType = eb.CreateTypeInfo().AsType(); + } + + return _floatEnumType; + } + } + + public static Type DoubleEnumType + { + get + { + if (_doubleEnumType == null) + { + EnumBuilder eb = GetModuleBuilder().DefineEnum("DoubleEnumType", TypeAttributes.Public, typeof(double)); + eb.DefineLiteral("A", 1.0); + eb.DefineLiteral("B", 2.0); + eb.DefineLiteral("C", 3.0); + _doubleEnumType = eb.CreateTypeInfo().AsType(); + } + + return _doubleEnumType; + } + } + + public static Type IntPtrEnumType + { + get + { + if (_intPtrEnumType == null) + { + EnumBuilder eb = GetModuleBuilder().DefineEnum("IntPtrEnumType", TypeAttributes.Public, typeof(IntPtr)); + _intPtrEnumType = eb.CreateTypeInfo().AsType(); + } + + return _intPtrEnumType; + } + } + + public static Type UIntPtrEnumType + { + get + { + if (_uintPtrEnumType == null) + { + EnumBuilder eb = GetModuleBuilder().DefineEnum("UIntPtrEnumType", TypeAttributes.Public, typeof(UIntPtr)); + _uintPtrEnumType = eb.CreateTypeInfo().AsType(); + } + + return _uintPtrEnumType; + } + } } public class FakeExpression : Expression From fab83437c46b7359838d5dfe9e3542ad6d9fb302 Mon Sep 17 00:00:00 2001 From: Hugh Bellamy Date: Fri, 25 Nov 2016 21:55:30 +0000 Subject: [PATCH 2/2] Address PR feedback --- .../tests/HelperTypes.cs | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/src/System.Linq.Expressions/tests/HelperTypes.cs b/src/System.Linq.Expressions/tests/HelperTypes.cs index 637890d65ef5..09afb35cf933 100644 --- a/src/System.Linq.Expressions/tests/HelperTypes.cs +++ b/src/System.Linq.Expressions/tests/HelperTypes.cs @@ -351,8 +351,6 @@ public static class NonCSharpTypes { private static Type _charEnumType; private static Type _boolEnumType; - private static Type _floatEnumType; - private static Type _doubleEnumType; private static Type _intPtrEnumType; private static Type _uintPtrEnumType; @@ -396,40 +394,6 @@ public static Type BoolEnumType } } - public static Type FloatEnumType - { - get - { - if (_floatEnumType == null) - { - EnumBuilder eb = GetModuleBuilder().DefineEnum("FloatEnumType", TypeAttributes.Public, typeof(float)); - eb.DefineLiteral("A", 1.0f); - eb.DefineLiteral("B", 2.0f); - eb.DefineLiteral("C", 3.0f); - _floatEnumType = eb.CreateTypeInfo().AsType(); - } - - return _floatEnumType; - } - } - - public static Type DoubleEnumType - { - get - { - if (_doubleEnumType == null) - { - EnumBuilder eb = GetModuleBuilder().DefineEnum("DoubleEnumType", TypeAttributes.Public, typeof(double)); - eb.DefineLiteral("A", 1.0); - eb.DefineLiteral("B", 2.0); - eb.DefineLiteral("C", 3.0); - _doubleEnumType = eb.CreateTypeInfo().AsType(); - } - - return _doubleEnumType; - } - } - public static Type IntPtrEnumType { get