Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement new API GetEnumValuesAsUnderlyingType #73057

Merged
merged 18 commits into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ public sealed override Array GetEnumValues()
return result;
}

public sealed override Array GetEnumValuesAsUnderlyingType()
{
if (!IsActualEnum)
throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");

return (Array)Enum.GetEnumInfo(this).ValuesAsUnderlyingType.Clone();
}

internal bool IsActualEnum
=> TryGetEEType(out EETypePtr eeType) && eeType.IsEnum;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(
/// </summary>
protected virtual IComparer Comparer => InvariantComparer.Default;

private static long GetEnumValue(bool isUnderlyingTypeUInt64, Enum enumVal, CultureInfo? culture)
private static long GetEnumValue(bool isUnderlyingTypeUInt64, object enumVal, CultureInfo? culture)
{
return isUnderlyingTypeUInt64 ?
unchecked((long)Convert.ToUInt64(enumVal, culture)) :
Expand All @@ -85,7 +85,7 @@ private static long GetEnumValue(bool isUnderlyingTypeUInt64, Enum enumVal, Cult
string[] values = strValue.Split(',');
foreach (string v in values)
{
convertedValue |= GetEnumValue(isUnderlyingTypeUInt64, (Enum)Enum.Parse(EnumType, v, true), culture);
convertedValue |= GetEnumValue(isUnderlyingTypeUInt64, Enum.Parse(EnumType, v, true), culture);
}
return Enum.ToObject(EnumType, convertedValue);
}
Expand Down Expand Up @@ -171,14 +171,14 @@ private static long GetEnumValue(bool isUnderlyingTypeUInt64, Enum enumVal, Cult
bool isUnderlyingTypeUInt64 = Enum.GetUnderlyingType(EnumType) == typeof(ulong);
List<Enum> flagValues = new List<Enum>();

Array objValues = Enum.GetValues(EnumType);
Array objValues = Enum.GetValuesAsUnderlyingType(EnumType);
long[] ulValues = new long[objValues.Length];
for (int idx = 0; idx < objValues.Length; idx++)
{
ulValues[idx] = GetEnumValue(isUnderlyingTypeUInt64, (Enum)objValues.GetValue(idx)!, culture);
ulValues[idx] = GetEnumValue(isUnderlyingTypeUInt64, objValues.GetValue(idx)!, culture);
}

long longValue = GetEnumValue(isUnderlyingTypeUInt64, (Enum)value, culture);
long longValue = GetEnumValue(isUnderlyingTypeUInt64, value, culture);
bool valueFound = true;
while (valueFound)
{
Expand Down
11 changes: 10 additions & 1 deletion src/libraries/System.Private.CoreLib/src/System/Enum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -315,13 +315,22 @@ public static TEnum[] GetValues<TEnum>() where TEnum : struct, Enum =>
(TEnum[])GetValues(typeof(TEnum));
#endif

[RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetValues<TEnum> overload instead.")]
[RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetValues<TEnum> overload or the GetValuesAsUnderlyingType method instead.")]
public static Array GetValues(Type enumType)
{
ArgumentNullException.ThrowIfNull(enumType);
return enumType.GetEnumValues();
}

public static Array GetValuesAsUnderlyingType<TEnum>() where TEnum : struct, Enum =>
GetValuesAsUnderlyingType(typeof(TEnum));
LakshanF marked this conversation as resolved.
Show resolved Hide resolved

public static Array GetValuesAsUnderlyingType(Type enumType)
{
ArgumentNullException.ThrowIfNull(enumType);
return enumType.GetEnumValuesAsUnderlyingType();
}
LakshanF marked this conversation as resolved.
Show resolved Hide resolved

[Intrinsic]
public bool HasFlag(Enum flag)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public sealed override Type MakeArrayType(int rank)
public sealed override string GetEnumName(object value) => throw new NotSupportedException(SR.NotSupported_SignatureType);
public sealed override string[] GetEnumNames() => throw new NotSupportedException(SR.NotSupported_SignatureType);
public sealed override Type GetEnumUnderlyingType() => throw new NotSupportedException(SR.NotSupported_SignatureType);
[RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use Enum.GetValues<TEnum> instead.")]
[RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetEnumValues<TEnum> overload or the GetEnumValuesAsUnderlyingType method instead.")]
public sealed override Array GetEnumValues() => throw new NotSupportedException(SR.NotSupported_SignatureType);
public sealed override Guid GUID => throw new NotSupportedException(SR.NotSupported_SignatureType);
protected sealed override TypeCode GetTypeCodeImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);
Expand Down
21 changes: 20 additions & 1 deletion src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public override string[] GetEnumNames()
return new ReadOnlySpan<string>(ret).ToArray();
}

[RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetValues<TEnum> overload instead.")]
[RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetEnumValues<TEnum> overload or the GetEnumValuesAsUnderlyingType method instead.")]
public override Array GetEnumValues()
{
if (!IsActualEnum)
Expand All @@ -139,6 +139,25 @@ public override Array GetEnumValues()
return ret;
}

public override Array GetEnumValuesAsUnderlyingType()
{
if (!IsActualEnum)
throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");

// Get all of the values
ulong[] values = Enum.InternalGetValues(this);

Array ret = Array.CreateInstance(Enum.InternalGetUnderlyingType(this), values.Length);

for (int i = 0; i < values.Length; i++)
{
object val = Enum.ToObject(this, values[i]);
ret.SetValue(val, i);
}
LakshanF marked this conversation as resolved.
Show resolved Hide resolved

return ret;
}

public override Type GetEnumUnderlyingType()
{
if (!IsActualEnum)
Expand Down
15 changes: 14 additions & 1 deletion src/libraries/System.Private.CoreLib/src/System/Type.cs
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ public virtual Type GetEnumUnderlyingType()
return fields[0].FieldType;
}

[RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use Enum.GetValues<TEnum> instead.")]
[RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetEnumValues<TEnum> overload or the GetEnumValuesAsUnderlyingType method instead.")]
public virtual Array GetEnumValues()
{
if (!IsEnum)
Expand All @@ -526,6 +526,19 @@ public virtual Array GetEnumValues()
throw NotImplemented.ByDesign;
}

[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode",
LakshanF marked this conversation as resolved.
Show resolved Hide resolved
Justification = "The runtime overrides this method with AOT compatible implementation")]
public virtual Array GetEnumValuesAsUnderlyingType()
{
if (!IsEnum)
throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");

Array enumValues = GetEnumValues();
Array ret = Array.CreateInstance(GetEnumUnderlyingType(), enumValues.Length);
Array.Copy(enumValues, ret, enumValues.Length);
return ret;
}

MichalStrehovsky marked this conversation as resolved.
Show resolved Hide resolved
[RequiresDynamicCode("The code for an array of the specified type might not be available.")]
public virtual Type MakeArrayType() => throw new NotSupportedException();
[RequiresDynamicCode("The code for an array of the specified type might not be available.")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,38 @@ public sealed override Type MakeArrayType(int rank)
private volatile RoType? _lazyUnderlyingEnumType;
public sealed override Array GetEnumValues() => throw new InvalidOperationException(SR.Arg_InvalidOperation_Reflection);

#if NET7_0_OR_GREATER
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2085:UnrecognizedReflectionPattern",
Justification = "Enum Types are not trimmed.")]
public override Array GetEnumValuesAsUnderlyingType()
LakshanF marked this conversation as resolved.
Show resolved Hide resolved
{
if (!IsEnum)
throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");

FieldInfo[] enumFields = this.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
jkotas marked this conversation as resolved.
Show resolved Hide resolved
jkotas marked this conversation as resolved.
Show resolved Hide resolved
int numValues = enumFields.Length;
Array ret = Type.GetTypeCode(GetEnumUnderlyingType()) switch
{
TypeCode.Byte => new byte[numValues],
TypeCode.SByte => new sbyte[numValues],
TypeCode.UInt16 => new ushort[numValues],
TypeCode.Int16 => new short[numValues],
TypeCode.UInt32 => new uint[numValues],
TypeCode.Int32 => new int[numValues],
TypeCode.UInt64 => new ulong[numValues],
TypeCode.Int64 => new long[numValues],
_ => throw new NotSupportedException(),
};

for (int i = 0; i < numValues; i++)
{
ret.SetValue(enumFields[i].GetRawConstantValue(), i);
}

return ret;
}
#endif

// No trust environment to apply these to.
public sealed override bool IsSecurityCritical => throw new InvalidOperationException(SR.InvalidOperation_IsSecurity);
public sealed override bool IsSecuritySafeCritical => throw new InvalidOperationException(SR.InvalidOperation_IsSecurity);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ public enum EI4 : int { }
public enum EU8 : ulong { }
public enum EI8 : long { }

public enum E_2_I4 : int { min=int.MinValue, zero=0, one=1, max=int.MaxValue}
public enum E_2_U4 : uint { min = uint.MinValue, zero = 0, one = 1, max = uint.MaxValue }

public class GenericEnumContainer<T>
{
public enum GenericEnum : short { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,28 @@ public static IEnumerable<object[]> GetEnumUnderlyingTypeData
}
}

[Fact]
public static void GetEnumValuesAsUnderlyingType()
{
var intEnumType = typeof(E_2_I4).Project();
int[] expectedIntValues = { int.MinValue, 0, 1, int.MaxValue };
Array intArr = intEnumType.GetEnumValuesAsUnderlyingType();
for (int i = 0; i < intArr.Length; i++)
{
Assert.Equal(expectedIntValues[i], intArr.GetValue(i));
Assert.Equal(Type.GetTypeCode(expectedIntValues[i].GetType()), Type.GetTypeCode(intArr.GetValue(i).GetType()));
}

var uintEnumType = typeof(E_2_U4).Project();
uint[] expectesUIntValues = { uint.MinValue, 0, 1, uint.MaxValue };
Array uintArr = uintEnumType.GetEnumValuesAsUnderlyingType();
for (int i = 0; i < uintArr.Length; i++)
{
Assert.Equal(expectesUIntValues[i], uintArr.GetValue(i));
Assert.Equal(Type.GetTypeCode(expectesUIntValues[i].GetType()), Type.GetTypeCode(uintArr.GetValue(i).GetType()));
}
}
jkotas marked this conversation as resolved.
Show resolved Hide resolved

[Theory]
[MemberData(nameof(GetTypeCodeTheoryData))]
public static void GettypeCode(TypeWrapper tw, TypeCode expectedTypeCode)
Expand Down
18 changes: 18 additions & 0 deletions src/libraries/System.Reflection/tests/TypeInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,24 @@ private static void GetEnumValues(Type enumType, Array expected)
Assert.Equal(expected, enumType.GetTypeInfo().GetEnumValues());
}

[Fact]
public static void GetEnumValuesAsUnderlyingType_Int()
{
GetEnumValuesAsUnderlyingType(typeof(IntEnum), new int[] { 1, 2, 10, 18, 45 });
}

[Fact]
public static void GetEnumValuesAsUnderlyingType_UInt()
{
GetEnumValues(typeof(UIntEnum), new uint[] { 1, 10 });
jkotas marked this conversation as resolved.
Show resolved Hide resolved
}

private static void GetEnumValuesAsUnderlyingType(Type enumType, Array expected)
{
Assert.Equal(expected, enumType.GetTypeInfo().GetEnumValuesAsUnderlyingType());
}


[Fact]
public void GetEnumValues_TypeNotEnum_ThrowsArgumentException()
{
Expand Down
7 changes: 5 additions & 2 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2312,9 +2312,11 @@ protected Enum() { }
public static string[] GetNames<TEnum>() where TEnum: struct, System.Enum { throw null; }
public System.TypeCode GetTypeCode() { throw null; }
public static System.Type GetUnderlyingType(System.Type enumType) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetValues<TEnum> overload instead.")]
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetValues<TEnum> overload or the GetValuesAsUnderlyingType method instead.")]
jkotas marked this conversation as resolved.
Show resolved Hide resolved
public static System.Array GetValues(System.Type enumType) { throw null; }
public static TEnum[] GetValues<TEnum>() where TEnum : struct, System.Enum { throw null; }
public static System.Array GetValuesAsUnderlyingType(System.Type enumType) { throw null; }
public static System.Array GetValuesAsUnderlyingType<TEnum>() where TEnum : struct, System.Enum { throw null; }
public bool HasFlag(System.Enum flag) { throw null; }
public static bool IsDefined(System.Type enumType, object value) { throw null; }
public static bool IsDefined<TEnum>(TEnum value) where TEnum : struct, System.Enum { throw null; }
Expand Down Expand Up @@ -5860,8 +5862,9 @@ protected Type() { }
public virtual string? GetEnumName(object value) { throw null; }
public virtual string[] GetEnumNames() { throw null; }
public virtual System.Type GetEnumUnderlyingType() { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use Enum.GetValues<TEnum> instead.")]
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetEnumValues<TEnum> overload or the GetEnumValuesAsUnderlyingType method instead.")]
public virtual System.Array GetEnumValues() { throw null; }
public virtual System.Array GetEnumValuesAsUnderlyingType() { throw null; }
[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents)]
public System.Reflection.EventInfo? GetEvent(string name) { throw null; }
[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicEvents | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents)]
Expand Down
70 changes: 70 additions & 0 deletions src/libraries/System.Runtime/tests/System/EnumTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,76 @@ public static void GetValues_NullEnumType_ThrowsArgumentNullException()
AssertExtensions.Throws<ArgumentNullException>("enumType", () => Enum.GetValues(null));
}

[Fact]
public void GetValuesAsUnderlyingType_InvokeSByteEnum_ReturnsExpected()
{
Array expected = new sbyte[] { 1, 2, sbyte.MaxValue, sbyte.MinValue };
Assert.Equal(expected, Enum.GetValuesAsUnderlyingType(typeof(SByteEnum)));
Assert.Equal(expected, Enum.GetValuesAsUnderlyingType<SByteEnum>());
}

[Fact]
public void GetValuesAsUnderlyingType_InvokeByteEnum_ReturnsExpected()
{
Array expected = new byte[] { byte.MinValue, 1, 2, byte.MaxValue };
Assert.Equal(expected, Enum.GetValuesAsUnderlyingType(typeof(ByteEnum)));
Assert.Equal(expected, Enum.GetValuesAsUnderlyingType<ByteEnum>());
}

[Fact]
public void GetValuesAsUnderlyingType_InvokeInt16Enum_ReturnsExpected()
{
Array expected = new short[] { 1, 2, short.MaxValue, short.MinValue };
Assert.Equal(expected, Enum.GetValuesAsUnderlyingType(typeof(Int16Enum)));
Assert.Equal(expected, Enum.GetValuesAsUnderlyingType<Int16Enum>());
}

[Fact]
public void GetValuesAsUnderlyingType_InvokeUInt16Enum_ReturnsExpected()
{
Array expected = new ushort[] { ushort.MinValue, 1, 2, ushort.MaxValue };
Assert.Equal(expected, Enum.GetValuesAsUnderlyingType(typeof(UInt16Enum)));
Assert.Equal(expected, Enum.GetValuesAsUnderlyingType<UInt16Enum>());
}

[Fact]
public void GetValuesAsUnderlyingType_InvokeInt32Enum_ReturnsExpected()
{
Array expected = new int[] { 1, 2, int.MaxValue, int.MinValue };
Assert.Equal(expected, Enum.GetValuesAsUnderlyingType(typeof(Int32Enum)));
Assert.Equal(expected, Enum.GetValuesAsUnderlyingType<Int32Enum>());
}

[Fact]
public void GetValuesAsUnderlyingType_InvokeUInt32Enum_ReturnsExpected()
{
Array expected = new uint[] { uint.MinValue, 1, 2, uint.MaxValue };
Assert.Equal(expected, Enum.GetValuesAsUnderlyingType(typeof(UInt32Enum)));
Assert.Equal(expected, Enum.GetValuesAsUnderlyingType<UInt32Enum>());
}

[Fact]
public void GetValuesAsUnderlyingType_InvokeInt64Enum_ReturnsExpected()
{
Array expected = new long[] { 1, 2, long.MaxValue, long.MinValue };
Assert.Equal(expected, Enum.GetValuesAsUnderlyingType(typeof(Int64Enum)));
Assert.Equal(expected, Enum.GetValuesAsUnderlyingType<Int64Enum>());
}

[Fact]
public void GetValuesAsUnderlyingType_InvokeUInt64Enum_ReturnsExpected()
{
Array expected = new ulong[] { ulong.MinValue, 1, 2, ulong.MaxValue };
Assert.Equal(expected, Enum.GetValuesAsUnderlyingType(typeof(UInt64Enum)));
Assert.Equal(expected, Enum.GetValuesAsUnderlyingType<UInt64Enum>());
}

[Fact]
public static void GetValuesAsUnderlyingType_NullEnumType_ThrowsArgumentNullException()
{
AssertExtensions.Throws<ArgumentNullException>("enumType", () => Enum.GetValuesAsUnderlyingType(null));
}

[Theory]
[InlineData(typeof(object))]
[InlineData(typeof(int))]
Expand Down
6 changes: 6 additions & 0 deletions src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1434,6 +1434,12 @@ public static void Run()
throw new Exception("GetValues");
}

Console.WriteLine("Enum.GetValuesAsUnderlyingType");
{
if (Enum.GetValuesAsUnderlyingType(typeof(Mine)).GetType() != typeof(int[]))
throw new Exception("Enum.GetValuesAsUnderlyingType");
}

Console.WriteLine("Pattern in LINQ expressions");
{
Type objType = typeof(object);
Expand Down