diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdConstant.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdConstant.cs index 1ef9ed2e331741..982569d8f152ce 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdConstant.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdConstant.cs @@ -72,7 +72,12 @@ internal static class MdConstant #endregion } - return Enum.ToObject(fieldType, defaultValue); + if (!fieldType.ContainsGenericParameters) + { + return Enum.ToObject(fieldType, defaultValue); + } + + // Open generic enum instances are not expected to exist. Fall through to underlying primitive value. } else if (fieldType == typeof(DateTime)) { @@ -103,28 +108,26 @@ internal static class MdConstant return new DateTime(defaultValue); } - else + + return corElementType switch { - return corElementType switch - { - CorElementType.ELEMENT_TYPE_VOID => DBNull.Value, - CorElementType.ELEMENT_TYPE_CHAR => *(char*)&buffer, - CorElementType.ELEMENT_TYPE_I1 => *(sbyte*)&buffer, - CorElementType.ELEMENT_TYPE_U1 => *(byte*)&buffer, - CorElementType.ELEMENT_TYPE_I2 => *(short*)&buffer, - CorElementType.ELEMENT_TYPE_U2 => *(ushort*)&buffer, - CorElementType.ELEMENT_TYPE_I4 => *(int*)&buffer, - CorElementType.ELEMENT_TYPE_U4 => *(uint*)&buffer, - CorElementType.ELEMENT_TYPE_I8 => buffer, - CorElementType.ELEMENT_TYPE_U8 => (ulong)buffer, - CorElementType.ELEMENT_TYPE_BOOLEAN => (*(int*)&buffer != 0), - CorElementType.ELEMENT_TYPE_R4 => *(float*)&buffer, - CorElementType.ELEMENT_TYPE_R8 => *(double*)&buffer, - CorElementType.ELEMENT_TYPE_STRING => stringVal ?? string.Empty, - CorElementType.ELEMENT_TYPE_CLASS => null, - _ => throw new FormatException(SR.Arg_BadLiteralFormat), - }; - } + CorElementType.ELEMENT_TYPE_VOID => DBNull.Value, + CorElementType.ELEMENT_TYPE_CHAR => *(char*)&buffer, + CorElementType.ELEMENT_TYPE_I1 => *(sbyte*)&buffer, + CorElementType.ELEMENT_TYPE_U1 => *(byte*)&buffer, + CorElementType.ELEMENT_TYPE_I2 => *(short*)&buffer, + CorElementType.ELEMENT_TYPE_U2 => *(ushort*)&buffer, + CorElementType.ELEMENT_TYPE_I4 => *(int*)&buffer, + CorElementType.ELEMENT_TYPE_U4 => *(uint*)&buffer, + CorElementType.ELEMENT_TYPE_I8 => buffer, + CorElementType.ELEMENT_TYPE_U8 => (ulong)buffer, + CorElementType.ELEMENT_TYPE_BOOLEAN => (*(int*)&buffer != 0), + CorElementType.ELEMENT_TYPE_R4 => *(float*)&buffer, + CorElementType.ELEMENT_TYPE_R8 => *(double*)&buffer, + CorElementType.ELEMENT_TYPE_STRING => stringVal ?? string.Empty, + CorElementType.ELEMENT_TYPE_CLASS => null, + _ => throw new FormatException(SR.Arg_BadLiteralFormat), + }; } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs index 33ebf9886745f0..7f8b412546d139 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs @@ -21,6 +21,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Numerics; using System.Reflection; using System.Reflection.Runtime.Assemblies; using System.Reflection.Runtime.BindingFlagSupport; @@ -431,24 +432,55 @@ public static void MakeTypedReference(object target, FieldInfo[] flds, out Type public static Assembly[] GetLoadedAssemblies() => RuntimeAssemblyInfo.GetLoadedAssemblies(); - public static EnumInfo GetEnumInfo(Type type, Func create) + internal static unsafe EnumInfo CreateAndCacheEnumInfo(RuntimeTypeInfo runtimeTypeInfo) { - RuntimeTypeInfo runtimeType = type.ToRuntimeTypeInfo(); - - var info = runtimeType.GenericCache as EnumInfo; - if (info != null) - return info; + Debug.Assert(runtimeTypeInfo.IsActualEnum); - ReflectionCoreExecution.ExecutionEnvironment.GetEnumInfo(runtimeType.TypeHandle, out string[] unsortedNames, out object[] unsortedValues, out bool isFlags); - - // Call into IntrospectiveSort directly to avoid the Comparer.Default codepath. - // That codepath would bring functionality to compare everything that was ever allocated in the program. - ArraySortHelper.IntrospectiveSort(unsortedValues, unsortedNames, EnumUnderlyingTypeComparer.Instance); + EnumInfo enumInfo; + if (runtimeTypeInfo.IsConstructedGenericType) + { + // Constructed generic instantiations may be missing MethodTable that we need to get + // the underlying type. Delegate them to generic type definitions that have it. + enumInfo = Enum.GetEnumInfo((RuntimeType)runtimeTypeInfo.GetGenericTypeDefinition()); + } + else + { + runtimeTypeInfo.GetEnumValuesAndNames(out string[] unsortedNames, out object[] unsortedValues, out bool isFlags); + + // Sort by unsigned storage type to match Enum ordering semantics. + // Call into IntrospectiveSort directly to avoid the Comparer.Default codepath. + // That codepath would bring functionality to compare everything that was ever allocated in the program. + ArraySortHelper.IntrospectiveSort(unsortedValues, unsortedNames, EnumUnderlyingTypeComparer.Instance); + + // Generic enums are guaranteed to have generic type definition MethodTable, + // so we can get the underlying type directly from it. + MethodTable* methodTable = runtimeTypeInfo.TypeHandle.ToMethodTable(); + Debug.Assert(methodTable->IsEnum); + enumInfo = methodTable->ElementType switch + { + EETypeElementType.SByte => CreateEnumInfoTyped(typeof(sbyte), unsortedNames, unsortedValues, isFlags), + EETypeElementType.Byte => CreateEnumInfoTyped(typeof(byte), unsortedNames, unsortedValues, isFlags), + EETypeElementType.Int16 => CreateEnumInfoTyped(typeof(short), unsortedNames, unsortedValues, isFlags), + EETypeElementType.UInt16 => CreateEnumInfoTyped(typeof(ushort), unsortedNames, unsortedValues, isFlags), + EETypeElementType.Int32 => CreateEnumInfoTyped(typeof(int), unsortedNames, unsortedValues, isFlags), + EETypeElementType.UInt32 => CreateEnumInfoTyped(typeof(uint), unsortedNames, unsortedValues, isFlags), + EETypeElementType.Int64 => CreateEnumInfoTyped(typeof(long), unsortedNames, unsortedValues, isFlags), + EETypeElementType.UInt64 => CreateEnumInfoTyped(typeof(ulong), unsortedNames, unsortedValues, isFlags), + _ => throw new NotSupportedException() + }; + } - info = create(RuntimeAugments.GetEnumUnderlyingType(type.TypeHandle), unsortedNames, unsortedValues, isFlags); + runtimeTypeInfo.GenericCache = enumInfo; + return enumInfo; + } - runtimeType.GenericCache = info; - return info; + private static EnumInfo CreateEnumInfoTyped(Type underlyingType, string[] names, object[] valuesAsObject, bool isFlags) + where TStorage : struct, INumber + { + var values = new TStorage[valuesAsObject.Length]; + for (int i = 0; i < valuesAsObject.Length; i++) + values[i] = (TStorage)valuesAsObject[i]; + return new EnumInfo(underlyingType, values, names, isFlags); } private sealed class EnumUnderlyingTypeComparer : IComparer diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs index ad13f8561375f3..1257caa7569cb1 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs @@ -77,7 +77,6 @@ public abstract class ExecutionEnvironment // Other //============================================================================================== public abstract FieldAccessor CreateLiteralFieldAccessor(object value, RuntimeTypeHandle fieldTypeHandle); - public abstract void GetEnumInfo(RuntimeTypeHandle typeHandle, out string[] names, out object[] values, out bool isFlags); public abstract IntPtr GetDynamicInvokeThunk(MethodBaseInvoker invoker); public abstract MethodInfo GetDelegateMethod(Delegate del); public abstract MethodBase GetMethodBaseFromStartAddressIfAvailable(IntPtr methodStartAddress); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs index 69750286092dcf..7016a7c2e1e5b7 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs @@ -355,38 +355,6 @@ public static unsafe void EnsureClassConstructorRun(IntPtr staticClassConstructi ClassConstructorRunner.EnsureClassConstructorRun(context); } - public static Type GetEnumUnderlyingType(RuntimeTypeHandle enumTypeHandle) - { - Debug.Assert(enumTypeHandle.ToMethodTable()->IsEnum); - - EETypeElementType elementType = enumTypeHandle.ToMethodTable()->ElementType; - switch (elementType) - { - case EETypeElementType.Boolean: - return typeof(bool); - case EETypeElementType.Char: - return typeof(char); - case EETypeElementType.SByte: - return typeof(sbyte); - case EETypeElementType.Byte: - return typeof(byte); - case EETypeElementType.Int16: - return typeof(short); - case EETypeElementType.UInt16: - return typeof(ushort); - case EETypeElementType.Int32: - return typeof(int); - case EETypeElementType.UInt32: - return typeof(uint); - case EETypeElementType.Int64: - return typeof(long); - case EETypeElementType.UInt64: - return typeof(ulong); - default: - throw new NotSupportedException(); - } - } - public static RuntimeTypeHandle GetRelatedParameterTypeHandle(RuntimeTypeHandle parameterTypeHandle) { MethodTable* elementType = parameterTypeHandle.ToMethodTable()->RelatedParameterType; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Enum.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Enum.NativeAot.cs index 0b8fd582e56e65..1db97d09db0bf4 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Enum.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Enum.NativeAot.cs @@ -9,7 +9,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Internal.Reflection.Augments; using Internal.Runtime; using Internal.Runtime.Augments; using Internal.Runtime.CompilerServices; @@ -22,19 +21,14 @@ namespace System public abstract partial class Enum : ValueType, IComparable, IFormattable, IConvertible { #pragma warning disable IDE0060 - internal static unsafe EnumInfo GetEnumInfo(RuntimeType enumType, bool getNames = true) + internal static EnumInfo GetEnumInfo(RuntimeType enumType, bool getNames = true) { Debug.Assert(enumType != null); Debug.Assert(enumType.IsEnum); - - return enumType.TypeHandle.ToMethodTable()->ElementType switch - { - EETypeElementType.SByte or EETypeElementType.Byte => GetEnumInfo(enumType), - EETypeElementType.Int16 or EETypeElementType.UInt16 => GetEnumInfo(enumType), - EETypeElementType.Int32 or EETypeElementType.UInt32 => GetEnumInfo(enumType), - EETypeElementType.Int64 or EETypeElementType.UInt64 => GetEnumInfo(enumType), - _ => throw new NotSupportedException(), - }; + var runtimeTypeInfo = enumType.GetRuntimeTypeInfo(); + if (runtimeTypeInfo.GenericCache is EnumInfo info) + return info; + return Internal.Reflection.Augments.ReflectionAugments.CreateAndCacheEnumInfo(runtimeTypeInfo); } internal static EnumInfo GetEnumInfo(RuntimeType enumType, bool getNames = true) @@ -48,17 +42,10 @@ internal static EnumInfo GetEnumInfo(RuntimeType enumType, b typeof(TStorage) == typeof(uint) || typeof(TStorage) == typeof(ulong)); - return (EnumInfo)ReflectionAugments.GetEnumInfo(enumType, - static (underlyingType, names, valuesAsObject, isFlags) => - { - // Only after we've sorted, create the underlying array. - var values = new TStorage[valuesAsObject.Length]; - for (int i = 0; i < valuesAsObject.Length; i++) - { - values[i] = (TStorage)valuesAsObject[i]; - } - return new EnumInfo(underlyingType, values, names, isFlags); - }); + var runtimeTypeInfo = enumType.GetRuntimeTypeInfo(); + if (runtimeTypeInfo.GenericCache is EnumInfo info) + return info; + return (EnumInfo)Internal.Reflection.Augments.ReflectionAugments.CreateAndCacheEnumInfo(runtimeTypeInfo); } #pragma warning restore @@ -144,9 +131,7 @@ internal static unsafe bool TryGetUnboxedValueOfEnumOrInteger(object value, out internal static Type InternalGetUnderlyingType(RuntimeType enumType) { - Debug.Assert(enumType is RuntimeType); - Debug.Assert(enumType.IsEnum); - + Debug.Assert(enumType.IsActualEnum); return GetEnumInfo(enumType).UnderlyingType; } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/NativeFormat/DefaultValueParser.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/NativeFormat/DefaultValueParser.cs index 1503cfdfaa38f3..633f966a221286 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/NativeFormat/DefaultValueParser.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/NativeFormat/DefaultValueParser.cs @@ -14,7 +14,7 @@ public static bool GetDefaultValueFromConstantIfAny(MetadataReader reader, Handl if (!constantHandle.IsNil) { defaultValue = constantHandle.ParseConstantValue(reader); - if ((!raw) && declaredType.IsEnum && defaultValue != null) + if ((!raw) && declaredType.IsEnum && defaultValue != null && !declaredType.ContainsGenericParameters) defaultValue = Enum.ToObject(declaredType, defaultValue); return true; } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.cs index 7284481f7bc4f9..d5dd1c87891ee5 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.cs @@ -204,6 +204,58 @@ public sealed override string Name return (this.ToType() == typeof(Nullable<>)) ? RuntimeGenericTypeParameters[0].ToType() : null; } + internal sealed override void GetEnumValuesAndNames(out string[] unsortedNames, out object[] unsortedValues, out bool isFlags) + { + Debug.Assert(IsActualEnum); + + // Count the number of static fields. The single instance field may or may not have metadata, + // so using `_typeDefinition.Fields.Count - 1` is not reliable. + int staticFieldCount = 0; + foreach (FieldHandle fieldHandle in _typeDefinition.Fields) + { + Field field = fieldHandle.GetField(_reader); + if (0 != (field.Flags & FieldAttributes.Static)) + staticFieldCount++; + } + + unsortedNames = new string[staticFieldCount]; + unsortedValues = new object[staticFieldCount]; + + int i = 0; + foreach (FieldHandle fieldHandle in _typeDefinition.Fields) + { + Field field = fieldHandle.GetField(_reader); + if (0 != (field.Flags & FieldAttributes.Static)) + { + unsortedNames[i] = field.Name.GetString(_reader); + Handle handle = field.DefaultValue; + unsortedValues[i] = handle.HandleType switch + { + HandleType.ConstantSByteValue => (object)(byte)handle.ToConstantSByteValueHandle(_reader).GetConstantSByteValue(_reader).Value, + HandleType.ConstantByteValue => handle.ToConstantByteValueHandle(_reader).GetConstantByteValue(_reader).Value, + HandleType.ConstantInt16Value => (ushort)handle.ToConstantInt16ValueHandle(_reader).GetConstantInt16Value(_reader).Value, + HandleType.ConstantUInt16Value => handle.ToConstantUInt16ValueHandle(_reader).GetConstantUInt16Value(_reader).Value, + HandleType.ConstantInt32Value => (uint)handle.ToConstantInt32ValueHandle(_reader).GetConstantInt32Value(_reader).Value, + HandleType.ConstantUInt32Value => handle.ToConstantUInt32ValueHandle(_reader).GetConstantUInt32Value(_reader).Value, + HandleType.ConstantInt64Value => (ulong)handle.ToConstantInt64ValueHandle(_reader).GetConstantInt64Value(_reader).Value, + HandleType.ConstantUInt64Value => handle.ToConstantUInt64ValueHandle(_reader).GetConstantUInt64Value(_reader).Value, + _ => throw new InvalidOperationException(), + }; + i++; + } + } + + isFlags = false; + foreach (CustomAttributeHandle cah in _typeDefinition.CustomAttributes) + { + if (cah.IsCustomAttributeOfType(_reader, ["System"], "FlagsAttribute")) + { + isFlags = true; + break; + } + } + } + internal sealed override RuntimeTypeInfo[] RuntimeGenericTypeParameters { get diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs index 46b620c9cfc26b..c15c37507ff0fa 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs @@ -401,6 +401,11 @@ public virtual Type GetGenericTypeDefinition() public virtual Type? GetNullableUnderlyingType() => null; + internal virtual void GetEnumValuesAndNames(out string[] unsortedNames, out object[] unsortedValues, out bool isFlags) + { + throw new NotSupportedException(); + } + public Type MakeArrayType() { // Do not implement this as a call to MakeArrayType(1) - they are not interchangeable. MakeArrayType() returns a diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs index 9a96d8205d451e..59d7f11651d47a 100644 --- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs @@ -13,7 +13,6 @@ using Internal.Reflection.Execution.MethodInvokers; using Internal.Reflection.Execution.PayForPlayExperience; using Internal.Reflection.Extensions.NonPortable; -using Internal.Runtime.Augments; using Internal.Runtime.TypeLoader; namespace Internal.Reflection.Execution @@ -68,34 +67,6 @@ public sealed override FieldAccessor CreateLiteralFieldAccessor(object value, Ru return new LiteralFieldAccessor(value, fieldTypeHandle); } - public sealed override void GetEnumInfo(RuntimeTypeHandle typeHandle, out string[] names, out object[] values, out bool isFlags) - { - // Handle the weird case of an enum type nested under a generic type that makes the - // enum itself generic - RuntimeTypeHandle typeDefHandle = typeHandle; - if (RuntimeAugments.IsGenericType(typeHandle)) - { - typeDefHandle = RuntimeAugments.GetGenericDefinition(typeHandle); - } - - QTypeDefinition qTypeDefinition = ReflectionExecution.ExecutionEnvironment.GetMetadataForNamedType(typeDefHandle); - - if (qTypeDefinition.IsNativeFormatMetadataBased) - { - NativeFormatEnumInfo.GetEnumValuesAndNames( - qTypeDefinition.NativeFormatReader, - qTypeDefinition.NativeFormatHandle, - out values, - out names, - out isFlags); - return; - } - names = Array.Empty(); - values = Array.Empty(); - isFlags = false; - return; - } - public override IntPtr GetDynamicInvokeThunk(MethodBaseInvoker invoker) { return ((MethodInvokerWithMethodInvokeInfo)invoker).MethodInvokeInfo.InvokeThunk; diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/NativeFormatEnumInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/NativeFormatEnumInfo.cs deleted file mode 100644 index d87bfad21e30b9..00000000000000 --- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/NativeFormatEnumInfo.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Numerics; -using System.Reflection; -using System.Reflection.Runtime.General; - -using Internal.Metadata.NativeFormat; -using Internal.Runtime.Augments; - -namespace Internal.Reflection.Execution -{ - internal static class NativeFormatEnumInfo - { - public static void GetEnumValuesAndNames(MetadataReader reader, TypeDefinitionHandle typeDefHandle, - out object[] unsortedBoxedValues, out string[] unsortedNames, out bool isFlags) - { - TypeDefinition typeDef = reader.GetTypeDefinition(typeDefHandle); - - // Count the number of static fields. The single instance field may or may not have metadata, - // so using `typeDef.Fields.Count - 1` is not reliable. - int staticFieldCount = 0; - foreach (FieldHandle fieldHandle in typeDef.Fields) - { - Field field = fieldHandle.GetField(reader); - if (0 != (field.Flags & FieldAttributes.Static)) - { - staticFieldCount++; - } - } - - unsortedNames = new string[staticFieldCount]; - unsortedBoxedValues = new object[staticFieldCount]; // TODO: Avoid boxing the values - - int i = 0; - foreach (FieldHandle fieldHandle in typeDef.Fields) - { - Field field = fieldHandle.GetField(reader); - if (0 != (field.Flags & FieldAttributes.Static)) - { - unsortedNames[i] = field.Name.GetString(reader); - var handle = field.DefaultValue; - unsortedBoxedValues[i] = handle.HandleType switch - { - HandleType.ConstantSByteValue => (object)(byte)handle.ToConstantSByteValueHandle(reader).GetConstantSByteValue(reader).Value, - HandleType.ConstantByteValue => handle.ToConstantByteValueHandle(reader).GetConstantByteValue(reader).Value, - HandleType.ConstantInt16Value => (ushort)handle.ToConstantInt16ValueHandle(reader).GetConstantInt16Value(reader).Value, - HandleType.ConstantUInt16Value => handle.ToConstantUInt16ValueHandle(reader).GetConstantUInt16Value(reader).Value, - HandleType.ConstantInt32Value => (uint)handle.ToConstantInt32ValueHandle(reader).GetConstantInt32Value(reader).Value, - HandleType.ConstantUInt32Value => handle.ToConstantUInt32ValueHandle(reader).GetConstantUInt32Value(reader).Value, - HandleType.ConstantInt64Value => (ulong)handle.ToConstantInt64ValueHandle(reader).GetConstantInt64Value(reader).Value, - HandleType.ConstantUInt64Value => handle.ToConstantUInt64ValueHandle(reader).GetConstantUInt64Value(reader).Value, - _ => throw new InvalidOperationException(), // unreachable - we would have thrown InvalidOperationException earlier - }; - i++; - } - } - - isFlags = false; - foreach (CustomAttributeHandle cah in typeDef.CustomAttributes) - { - if (cah.IsCustomAttributeOfType(reader, ["System"], "FlagsAttribute")) - { - isFlags = true; - break; - } - } - } - } -} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj index 823a6e0564f095..5648a7deed20c2 100644 --- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj @@ -17,7 +17,6 @@ - diff --git a/src/libraries/System.Runtime/tests/System.Reflection.Tests/ParameterInfoTests.cs b/src/libraries/System.Runtime/tests/System.Reflection.Tests/ParameterInfoTests.cs index 6b2326f811d58e..98d5ab4d0d4f05 100644 --- a/src/libraries/System.Runtime/tests/System.Reflection.Tests/ParameterInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System.Reflection.Tests/ParameterInfoTests.cs @@ -320,6 +320,39 @@ public void DefaultValue(Type type, string name, int index, object? expected) Assert.Equal(expected, parameterInfo.DefaultValue); } + [Fact] + public void DefaultValue_EnumNestedInGenericTypeOnGenericMethodDefinition() + { + Action.NestedEnum> action = MethodWithNestedGenericEnumDefault; + ParameterInfo parameterInfo = action.Method.GetGenericMethodDefinition().GetParameters()[0]; + + object defaultValue = parameterInfo.DefaultValue; + Assert.Equal(parameterInfo.ParameterType.GetEnumUnderlyingType(), defaultValue.GetType()); + Assert.Equal((byte)0, Convert.ToByte(defaultValue)); + } + + [Fact] + public void EnumAPIs_OnOpenGenericEnumType() + { + Type openEnumType = typeof(NestedGenericEnumContainer<>.NestedEnum); + + Assert.Equal(typeof(byte), openEnumType.GetEnumUnderlyingType()); + + string[] names = openEnumType.GetEnumNames(); + Assert.Equal(new[] { "Zero", "One" }, names); + + byte[] values = Assert.IsType(openEnumType.GetEnumValuesAsUnderlyingType()); + Assert.Equal(new byte[] { 0, 1 }, values); + Assert.Equal("Zero", openEnumType.GetEnumName((byte)0)); + Assert.Equal("One", openEnumType.GetEnumName((byte)1)); + + Assert.True(openEnumType.IsEnumDefined("Zero")); + Assert.False(openEnumType.IsEnumDefined("Two")); + + // GetEnumValues requires Array.CreateInstance which does not support open generic types + Assert.Throws(() => openEnumType.GetEnumValues()); + } + [Theory] [InlineData(typeof(ParameterInfoMetadata), "MethodWithDefaultDateTime", 0, null)] public void DefaultValue_broken_on_NETFX(Type type, string name, int index, object? expected) @@ -638,6 +671,17 @@ public void GenericMethod(T t) { } public string GenericMethodWithDefault(int i, T t = default(T)) { return "somestring"; } } + private static void MethodWithNestedGenericEnumDefault(NestedGenericEnumContainer.NestedEnum arg = 0) { } + + private class NestedGenericEnumContainer + { + public enum NestedEnum : byte + { + Zero, + One + } + } + private class MyAttribute : Attribute { public int Value {get; private set;}