diff --git a/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs index 844375b9312ce..b3d9ce0a2954e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs @@ -11,7 +11,6 @@ namespace System { public abstract partial class Enum { - // This returns 0 for all values for float/double/nint/nuint. [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Enum_GetValuesAndNames")] private static partial void GetEnumValuesAndNames(QCallTypeHandle enumType, ObjectHandleOnStack values, ObjectHandleOnStack names, Interop.BOOL getNames); @@ -84,17 +83,19 @@ private static EnumInfo GetEnumInfo(RuntimeT [MethodImpl(MethodImplOptions.NoInlining)] static EnumInfo InitializeEnumInfo(RuntimeType enumType, bool getNames) { - ulong[]? uint64Values = null; + TUnderlyingValue[]? values = null; string[]? names = null; - RuntimeTypeHandle enumTypeHandle = enumType.TypeHandle; + GetEnumValuesAndNames( - new QCallTypeHandle(ref enumTypeHandle), - ObjectHandleOnStack.Create(ref uint64Values), + new QCallTypeHandle(ref enumType), + ObjectHandleOnStack.Create(ref values), ObjectHandleOnStack.Create(ref names), getNames ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); - bool hasFlagsAttribute = enumType.IsDefined(typeof(FlagsAttribute), inherit: false); - TUnderlyingValue[] values = ToUnderlyingValues(uint64Values!); + Debug.Assert(values!.GetType() == typeof(TUnderlyingValue[])); + Debug.Assert(!getNames || names!.GetType() == typeof(string[])); + + bool hasFlagsAttribute = enumType.IsDefined(typeof(FlagsAttribute), inherit: false); var entry = new EnumInfo(hasFlagsAttribute, values, names!); enumType.GenericCache = entry; diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index c993784bca60b..4abe63d6d9cf3 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -1919,26 +1919,6 @@ struct TempEnumValue UINT64 value; }; -//******************************************************************************* -class TempEnumValueSorter : public CQuickSort -{ -public: - TempEnumValueSorter(TempEnumValue *pArray, SSIZE_T iCount) - : CQuickSort(pArray, iCount) { LIMITED_METHOD_CONTRACT; } - - int Compare(TempEnumValue *pFirst, TempEnumValue *pSecond) - { - LIMITED_METHOD_CONTRACT; - - if (pFirst->value == pSecond->value) - return 0; - if (pFirst->value > pSecond->value) - return 1; - else - return -1; - } -}; - extern "C" void QCALLTYPE Enum_GetValuesAndNames(QCall::TypeHandle pEnumType, QCall::ObjectHandleOnStack pReturnValues, QCall::ObjectHandleOnStack pReturnNames, BOOL fGetNames) { QCALL_CONTRACT; @@ -1946,9 +1926,7 @@ extern "C" void QCALLTYPE Enum_GetValuesAndNames(QCall::TypeHandle pEnumType, QC BEGIN_QCALL; TypeHandle th = pEnumType.AsTypeHandle(); - - if (!th.IsEnum()) - COMPlusThrow(kArgumentException, W("Arg_MustBeEnum")); + _ASSERTE(th.IsEnum()); MethodTable *pMT = th.AsMethodTable(); @@ -1959,81 +1937,30 @@ extern "C" void QCALLTYPE Enum_GetValuesAndNames(QCall::TypeHandle pEnumType, QC HENUMInternalHolder fieldEnum(pImport); fieldEnum.EnumInit(mdtFieldDef, pMT->GetCl()); - // - // Note that we're fine treating signed types as unsigned, because all we really - // want to do is sort them based on a convenient strong ordering. - // - - BOOL sorted = TRUE; - - CorElementType type = pMT->GetInternalCorElementType(); + CorElementType type = pMT->GetClass()->GetInternalCorElementType(); mdFieldDef field; while (pImport->EnumNext(&fieldEnum, &field)) { DWORD dwFlags; IfFailThrow(pImport->GetFieldDefProps(field, &dwFlags)); - if (IsFdStatic(dwFlags)) - { - TempEnumValue temp; - - if (fGetNames) - IfFailThrow(pImport->GetNameOfFieldDef(field, &temp.name)); - - UINT64 value = 0; + if (!IsFdStatic(dwFlags)) + continue; - MDDefaultValue defaultValue; - IfFailThrow(pImport->GetDefaultValue(field, &defaultValue)); + TempEnumValue temp; - // The following code assumes that the address of all union members is the same. - static_assert_no_msg(offsetof(MDDefaultValue, m_byteValue) == offsetof(MDDefaultValue, m_usValue)); - static_assert_no_msg(offsetof(MDDefaultValue, m_ulValue) == offsetof(MDDefaultValue, m_ullValue)); - PVOID pValue = &defaultValue.m_byteValue; - - switch (type) { - case ELEMENT_TYPE_I1: - value = *((INT8 *)pValue); - break; - - case ELEMENT_TYPE_U1: - case ELEMENT_TYPE_BOOLEAN: - value = *((UINT8 *)pValue); - break; - - case ELEMENT_TYPE_I2: - value = *((INT16 *)pValue); - break; - - case ELEMENT_TYPE_U2: - case ELEMENT_TYPE_CHAR: - value = *((UINT16 *)pValue); - break; - - case ELEMENT_TYPE_I4: - IN_TARGET_32BIT(case ELEMENT_TYPE_I:) - value = *((INT32 *)pValue); - break; + if (fGetNames) + IfFailThrow(pImport->GetNameOfFieldDef(field, &temp.name)); - case ELEMENT_TYPE_U4: - IN_TARGET_32BIT(case ELEMENT_TYPE_U:) - value = *((UINT32 *)pValue); - break; + MDDefaultValue defaultValue = { }; + IfFailThrow(pImport->GetDefaultValue(field, &defaultValue)); - case ELEMENT_TYPE_I8: - case ELEMENT_TYPE_U8: - IN_TARGET_64BIT(case ELEMENT_TYPE_I:) - IN_TARGET_64BIT(case ELEMENT_TYPE_U:) - value = *((INT64 *)pValue); - break; + // The following code assumes that the address of all union members is the same. + static_assert_no_msg(offsetof(MDDefaultValue, m_byteValue) == offsetof(MDDefaultValue, m_usValue)); + static_assert_no_msg(offsetof(MDDefaultValue, m_ulValue) == offsetof(MDDefaultValue, m_ullValue)); + temp.value = defaultValue.m_ullValue; - default: - break; - } - - temp.value = value; - - temps.Append(temp); - } + temps.Append(temp); } TempEnumValue * pTemps = &(temps[0]); @@ -2043,7 +1970,7 @@ extern "C" void QCALLTYPE Enum_GetValuesAndNames(QCall::TypeHandle pEnumType, QC GCX_COOP(); struct gc { - I8ARRAYREF values; + BASEARRAYREF values; PTRARRAYREF names; } gc; gc.values = NULL; @@ -2052,12 +1979,14 @@ extern "C" void QCALLTYPE Enum_GetValuesAndNames(QCall::TypeHandle pEnumType, QC GCPROTECT_BEGIN(gc); { - gc.values = (I8ARRAYREF) AllocatePrimitiveArray(ELEMENT_TYPE_U8, cFields); + // The managed side expects ELEMENT_TYPE_U1 as the underlying type for boolean + gc.values = (BASEARRAYREF) AllocatePrimitiveArray((type == ELEMENT_TYPE_BOOLEAN) ? ELEMENT_TYPE_U1 : type, cFields); - INT64 *pToValues = gc.values->GetDirectPointerToNonObjectElements(); + BYTE* pToValues = gc.values->GetDataPtr(); + size_t elementSize = gc.values->GetComponentSize(); - for (DWORD i = 0; i < cFields; i++) { - pToValues[i] = pTemps[i].value; + for (DWORD i = 0; i < cFields; i++, pToValues += elementSize) { + memcpyNoGCRefs(pToValues, &pTemps[i].value, elementSize); } pReturnValues.Set(gc.values); diff --git a/src/libraries/System.Private.CoreLib/src/System/Enum.cs b/src/libraries/System.Private.CoreLib/src/System/Enum.cs index f283ff10125f2..d8dc2791c370a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Enum.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Enum.cs @@ -846,14 +846,14 @@ static bool HandleRareTypes(RuntimeType rt, ReadOnlySpan value, bool ignor if (underlyingType == typeof(float)) { parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out float localResult); - result = parsed ? InternalBoxEnum(rt, (long)localResult) : null; + result = parsed ? InternalBoxEnum(rt, BitConverter.SingleToInt32Bits(localResult)) : null; return parsed; } if (underlyingType == typeof(double)) { parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out double localResult); - result = parsed ? InternalBoxEnum(rt, (long)localResult) : null; + result = parsed ? InternalBoxEnum(rt, BitConverter.DoubleToInt64Bits(localResult)) : null; return parsed; } @@ -2193,29 +2193,6 @@ private static void WriteMultipleFoundFlagsNames(string[] names, ReadOnlySpanCreates a new TUnderlyingValue[] from a ulong[] array of values. - private static TUnderlyingValue[] ToUnderlyingValues(ulong[] uint64Values) - where TUnderlyingValue : struct, INumber - { - TUnderlyingValue[] values; - - if (typeof(TUnderlyingValue) == typeof(ulong)) - { - values = (TUnderlyingValue[])(object)uint64Values; - } - else - { - values = new TUnderlyingValue[uint64Values.Length]; - - for (int i = 0; i < values.Length; i++) - { - values[i] = TUnderlyingValue.CreateTruncating(uint64Values[i]); - } - } - - return values; - } - private static RuntimeType ValidateRuntimeType(Type enumType) { ArgumentNullException.ThrowIfNull(enumType); diff --git a/src/libraries/System.Runtime/tests/System/EnumTests.cs b/src/libraries/System.Runtime/tests/System/EnumTests.cs index c9c224e9b6269..00358a275ec2f 100644 --- a/src/libraries/System.Runtime/tests/System/EnumTests.cs +++ b/src/libraries/System.Runtime/tests/System/EnumTests.cs @@ -88,16 +88,13 @@ public static IEnumerable Parse_TestData() yield return new object[] { "Value1", false, Enum.ToObject(s_boolEnumType, true) }; yield return new object[] { "vaLue2", true, Enum.ToObject(s_boolEnumType, false) }; - if (!PlatformDetection.IsMonoRuntime) // [ActiveIssue("https://github.com/dotnet/runtime/issues/29266")] - { - // Single - parses successfully, but doesn't properly represent the underlying value - yield return new object[] { "Value1", false, Enum.GetValues(s_floatEnumType).GetValue(0) }; - yield return new object[] { "vaLue2", true, Enum.GetValues(s_floatEnumType).GetValue(0) }; + // Single + yield return new object[] { "Value1", false, Enum.GetValues(s_floatEnumType).GetValue(1) }; + yield return new object[] { "vaLue2", true, Enum.GetValues(s_floatEnumType).GetValue(2) }; - // Double - parses successfully, but doesn't properly represent the underlying value - yield return new object[] { "Value1", false, Enum.GetValues(s_doubleEnumType).GetValue(0) }; - yield return new object[] { "vaLue2", true, Enum.GetValues(s_doubleEnumType).GetValue(0) }; - } + // Double + yield return new object[] { "Value1", false, Enum.GetValues(s_doubleEnumType).GetValue(1) }; + yield return new object[] { "vaLue2", true, Enum.GetValues(s_doubleEnumType).GetValue(2) }; } // SimpleEnum @@ -1409,16 +1406,16 @@ public void GetNames_InvokeBoolEnum_ReturnsExpected() [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void GetNames_InvokeSingleEnum_ReturnsExpected() { - var expected = new string[] { "Value1", "Value2", "Value0x3f06", "Value0x3000", "Value0x0f06", "Value0x1000", "Value0x0000", "Value0x0010", "Value0x3f16" }; - Assert.Equal(new HashSet(expected), new HashSet(Enum.GetNames(s_floatEnumType))); // using sets because coreclr returns 0 for all float enum values, affecting sort order + var expected = new string[] { "Value0x0000", "Value1", "Value2", "Value0x0010", "Value0x0f06", "Value0x1000", "Value0x3000", "Value0x3f06", "Value0x3f16" }; + Assert.Equal(expected, Enum.GetNames(s_floatEnumType)); Assert.NotSame(Enum.GetNames(s_floatEnumType), Enum.GetNames(s_floatEnumType)); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void GetNames_InvokeDoubleEnum_ReturnsExpected() { - var expected = new string[] { "Value1", "Value2", "Value0x3f06", "Value0x3000", "Value0x0f06", "Value0x1000", "Value0x0000", "Value0x0010", "Value0x3f16" }; - Assert.Equal(new HashSet(expected), new HashSet(Enum.GetNames(s_doubleEnumType))); // using sets because coreclr returns 0 for all double enum values, affecting sort order + var expected = new string[] { "Value0x0000", "Value1", "Value2", "Value0x0010", "Value0x0f06", "Value0x1000", "Value0x3000", "Value0x3f06", "Value0x3f16" }; + Assert.Equal(expected, Enum.GetNames(s_doubleEnumType)); Assert.NotSame(Enum.GetNames(s_doubleEnumType), Enum.GetNames(s_doubleEnumType)); } @@ -1554,7 +1551,7 @@ public void GetValues_InvokeBoolEnum_ReturnsExpected() [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void GetValues_InvokeSingleEnum_ReturnsExpected() { - var expected = new object[] { Enum.Parse(s_floatEnumType, "Value1"), Enum.Parse(s_floatEnumType, "Value2"), Enum.Parse(s_floatEnumType, "Value0x3f06"), Enum.Parse(s_floatEnumType, "Value0x3000"), Enum.Parse(s_floatEnumType, "Value0x0f06"), Enum.Parse(s_floatEnumType, "Value0x1000"), Enum.Parse(s_floatEnumType, "Value0x0000"), Enum.Parse(s_floatEnumType, "Value0x0010"), Enum.Parse(s_floatEnumType, "Value0x3f16") }; + var expected = new object[] { Enum.Parse(s_floatEnumType, "Value0x0000"), Enum.Parse(s_floatEnumType, "Value1"), Enum.Parse(s_floatEnumType, "Value2"), Enum.Parse(s_floatEnumType, "Value0x0010"), Enum.Parse(s_floatEnumType, "Value0x0f06"), Enum.Parse(s_floatEnumType, "Value0x1000"), Enum.Parse(s_floatEnumType, "Value0x3000"), Enum.Parse(s_floatEnumType, "Value0x3f06"), Enum.Parse(s_floatEnumType, "Value0x3f16") }; Assert.Equal(expected, Enum.GetValues(s_floatEnumType)); Assert.NotSame(Enum.GetValues(s_floatEnumType), Enum.GetValues(s_floatEnumType)); } @@ -1562,7 +1559,7 @@ public void GetValues_InvokeSingleEnum_ReturnsExpected() [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void GetValues_InvokeDoubleEnum_ReturnsExpected() { - var expected = new object[] { Enum.Parse(s_doubleEnumType, "Value1"), Enum.Parse(s_doubleEnumType, "Value2"), Enum.Parse(s_doubleEnumType, "Value0x3f06"), Enum.Parse(s_doubleEnumType, "Value0x3000"), Enum.Parse(s_doubleEnumType, "Value0x0f06"), Enum.Parse(s_doubleEnumType, "Value0x1000"), Enum.Parse(s_doubleEnumType, "Value0x0000"), Enum.Parse(s_doubleEnumType, "Value0x0010"), Enum.Parse(s_doubleEnumType, "Value0x3f16") }; + var expected = new object[] { Enum.Parse(s_doubleEnumType, "Value0x0000"), Enum.Parse(s_doubleEnumType, "Value1"), Enum.Parse(s_doubleEnumType, "Value2"), Enum.Parse(s_doubleEnumType, "Value0x0010"), Enum.Parse(s_doubleEnumType, "Value0x0f06"), Enum.Parse(s_doubleEnumType, "Value0x1000"), Enum.Parse(s_doubleEnumType, "Value0x3000"), Enum.Parse(s_doubleEnumType, "Value0x3f06"), Enum.Parse(s_doubleEnumType, "Value0x3f16") }; Assert.Equal(expected, Enum.GetValues(s_doubleEnumType)); Assert.NotSame(Enum.GetValues(s_doubleEnumType), Enum.GetValues(s_doubleEnumType)); } diff --git a/src/mono/System.Private.CoreLib/src/System/Enum.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Enum.Mono.cs index 983dd3022669c..5f6773d2a4c01 100644 --- a/src/mono/System.Private.CoreLib/src/System/Enum.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Enum.Mono.cs @@ -41,6 +41,29 @@ internal static RuntimeType InternalGetUnderlyingType(RuntimeType enumType) return res!; } + /// Creates a new TUnderlyingValue[] from a ulong[] array of values. + private static TUnderlyingValue[] ToUnderlyingValues(ulong[] uint64Values) + where TUnderlyingValue : struct, INumber + { + TUnderlyingValue[] values; + + if (typeof(TUnderlyingValue) == typeof(ulong)) + { + values = (TUnderlyingValue[])(object)uint64Values; + } + else + { + values = new TUnderlyingValue[uint64Values.Length]; + + for (int i = 0; i < values.Length; i++) + { + values[i] = TUnderlyingValue.CreateTruncating(uint64Values[i]); + } + } + + return values; + } + private static EnumInfo GetEnumInfo(RuntimeType enumType, bool getNames = true) where TUnderlyingValue : struct, INumber {