Skip to content

Commit a938ea8

Browse files
authored
Revert "Rewrite Enum and add {ISpanFormattable}.TryFormat (#78580)"
This reverts commit 62f3eb2.
1 parent c4b37da commit a938ea8

File tree

38 files changed

+1357
-2363
lines changed

38 files changed

+1357
-2363
lines changed

src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs

+8-22
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics;
5-
using System.Numerics;
5+
using System.Diagnostics.CodeAnalysis;
66
using System.Reflection;
77
using System.Runtime.CompilerServices;
88
using System.Runtime.InteropServices;
@@ -20,15 +20,6 @@ public abstract partial class Enum
2020
[MethodImpl(MethodImplOptions.InternalCall)]
2121
private static extern unsafe CorElementType InternalGetCorElementType(MethodTable* pMT);
2222

23-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
24-
private static unsafe CorElementType InternalGetCorElementType(RuntimeType rt)
25-
{
26-
Debug.Assert(rt.IsActualEnum);
27-
CorElementType elementType = InternalGetCorElementType((MethodTable*)rt.GetUnderlyingNativeHandle());
28-
GC.KeepAlive(rt);
29-
return elementType;
30-
}
31-
3223
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3324
private unsafe CorElementType InternalGetCorElementType()
3425
{
@@ -82,31 +73,26 @@ internal static unsafe RuntimeType InternalGetUnderlyingType(RuntimeType enumTyp
8273
}
8374

8475
[MethodImpl(MethodImplOptions.AggressiveInlining)]
85-
private static EnumInfo<TUnderlyingValue> GetEnumInfo<TUnderlyingValue>(RuntimeType enumType, bool getNames = true)
86-
where TUnderlyingValue : struct, INumber<TUnderlyingValue>
76+
private static EnumInfo GetEnumInfo(RuntimeType enumType, bool getNames = true)
8777
{
88-
return enumType.GenericCache is EnumInfo<TUnderlyingValue> info && (!getNames || info.Names is not null) ?
78+
return enumType.GenericCache is EnumInfo info && (!getNames || info.Names is not null) ?
8979
info :
9080
InitializeEnumInfo(enumType, getNames);
9181

9282
[MethodImpl(MethodImplOptions.NoInlining)]
93-
static EnumInfo<TUnderlyingValue> InitializeEnumInfo(RuntimeType enumType, bool getNames)
83+
static EnumInfo InitializeEnumInfo(RuntimeType enumType, bool getNames)
9484
{
95-
TUnderlyingValue[]? values = null;
85+
ulong[]? values = null;
9686
string[]? names = null;
97-
87+
RuntimeTypeHandle enumTypeHandle = enumType.TypeHandle;
9888
GetEnumValuesAndNames(
99-
new QCallTypeHandle(ref enumType),
89+
new QCallTypeHandle(ref enumTypeHandle),
10090
ObjectHandleOnStack.Create(ref values),
10191
ObjectHandleOnStack.Create(ref names),
10292
getNames ? Interop.BOOL.TRUE : Interop.BOOL.FALSE);
103-
104-
Debug.Assert(values!.GetType() == typeof(TUnderlyingValue[]));
105-
Debug.Assert(!getNames || names!.GetType() == typeof(string[]));
106-
10793
bool hasFlagsAttribute = enumType.IsDefined(typeof(FlagsAttribute), inherit: false);
10894

109-
var entry = new EnumInfo<TUnderlyingValue>(hasFlagsAttribute, values, names!);
95+
var entry = new EnumInfo(hasFlagsAttribute, values!, names!);
11096
enumType.GenericCache = entry;
11197
return entry;
11298
}

src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml

+1-5
Original file line numberDiff line numberDiff line change
@@ -824,10 +824,6 @@
824824
<DiagnosticId>CP0001</DiagnosticId>
825825
<Target>T:System.Reflection.EnumInfo</Target>
826826
</Suppression>
827-
<Suppression>
828-
<DiagnosticId>CP0001</DiagnosticId>
829-
<Target>T:System.Reflection.EnumInfo`1</Target>
830-
</Suppression>
831827
<Suppression>
832828
<DiagnosticId>CP0001</DiagnosticId>
833829
<Target>T:System.Reflection.Runtime.General.MetadataReaderExtensions</Target>
@@ -976,4 +972,4 @@
976972
<DiagnosticId>CP0016</DiagnosticId>
977973
<Target>M:System.Runtime.InteropServices.Marshal.GetObjectForNativeVariant``1(System.IntPtr)-&gt;T?:[T:System.Diagnostics.CodeAnalysis.MaybeNullAttribute]</Target>
978974
</Suppression>
979-
</Suppressions>
975+
</Suppressions>

src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs

+1-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
using System.Diagnostics;
2222
using System.Diagnostics.CodeAnalysis;
2323
using System.Globalization;
24-
using System.Numerics;
2524

2625
using EETypeElementType = Internal.Runtime.EETypeElementType;
2726

@@ -165,8 +164,7 @@ public abstract object ActivatorCreateInstance(
165164

166165
public abstract Assembly[] GetLoadedAssemblies();
167166

168-
public abstract EnumInfo<TUnderlyingValue> GetEnumInfo<TUnderlyingValue>(Type type)
169-
where TUnderlyingValue : struct, INumber<TUnderlyingValue>;
167+
public abstract EnumInfo GetEnumInfo(Type type);
170168

171169
public abstract DynamicInvokeInfo GetDelegateDynamicInvokeInfo(Type type);
172170
}

src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs

+1-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System.Reflection;
77
using System.Collections.Generic;
88
using System.Diagnostics.CodeAnalysis;
9-
using System.Numerics;
109
using System.Reflection.Runtime.General;
1110
using System.Reflection.Runtime.TypeInfos;
1211
using System.Runtime.CompilerServices;
@@ -98,9 +97,7 @@ public abstract class ExecutionEnvironment
9897
// Other
9998
//==============================================================================================
10099
public abstract FieldAccessor CreateLiteralFieldAccessor(object value, RuntimeTypeHandle fieldTypeHandle);
101-
public abstract EnumInfo<TUnderlyingValue> GetEnumInfo<TUnderlyingValue>(RuntimeTypeHandle typeHandle)
102-
where TUnderlyingValue : struct, INumber<TUnderlyingValue>;
103-
100+
public abstract EnumInfo GetEnumInfo(RuntimeTypeHandle typeHandle);
104101
public abstract IntPtr GetDynamicInvokeThunk(MethodInvoker invoker);
105102

106103
//==============================================================================================

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Enum.NativeAot.cs

+9-31
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@
33

44
using System.Diagnostics;
55
using System.Diagnostics.CodeAnalysis;
6-
using System.Numerics;
76
using System.Reflection;
87
using System.Runtime;
98
using System.Runtime.CompilerServices;
10-
using Internal.Runtime.Augments;
119
using Internal.Runtime.CompilerServices;
1210
using Internal.Reflection.Augments;
1311

@@ -25,29 +23,7 @@ internal static EnumInfo GetEnumInfo(Type enumType, bool getNames = true)
2523
Debug.Assert(enumType is RuntimeType);
2624
Debug.Assert(enumType.IsEnum);
2725

28-
RuntimeType rt = (RuntimeType)enumType;
29-
return Type.GetTypeCode(RuntimeAugments.GetEnumUnderlyingType(rt.TypeHandle)) switch
30-
{
31-
TypeCode.SByte => GetEnumInfo<sbyte>(rt),
32-
TypeCode.Byte => GetEnumInfo<byte>(rt),
33-
TypeCode.Int16 => GetEnumInfo<short>(rt),
34-
TypeCode.UInt16 => GetEnumInfo<ushort>(rt),
35-
TypeCode.Int32 => GetEnumInfo<int>(rt),
36-
TypeCode.UInt32 => GetEnumInfo<uint>(rt),
37-
TypeCode.Int64 => GetEnumInfo<long>(rt),
38-
TypeCode.UInt64 => GetEnumInfo<ulong>(rt),
39-
_ => throw new NotSupportedException(),
40-
};
41-
}
42-
43-
internal static EnumInfo<TUnderlyingValue> GetEnumInfo<TUnderlyingValue>(Type enumType, bool getNames = true)
44-
where TUnderlyingValue : struct, INumber<TUnderlyingValue>
45-
{
46-
Debug.Assert(enumType != null);
47-
Debug.Assert(enumType is RuntimeType);
48-
Debug.Assert(enumType.IsEnum);
49-
50-
return ReflectionAugments.ReflectionCoreCallbacks.GetEnumInfo<TUnderlyingValue>(enumType);
26+
return ReflectionAugments.ReflectionCoreCallbacks.GetEnumInfo(enumType);
5127
}
5228
#pragma warning restore
5329

@@ -56,12 +32,6 @@ private static object InternalBoxEnum(Type enumType, long value)
5632
return ToObject(enumType.TypeHandle.ToEETypePtr(), value);
5733
}
5834

59-
private static CorElementType InternalGetCorElementType(RuntimeType rt)
60-
{
61-
Debug.Assert(rt.IsActualEnum);
62-
return rt.TypeHandle.ToEETypePtr().CorElementType;
63-
}
64-
6535
private CorElementType InternalGetCorElementType()
6636
{
6737
return this.GetEETypePtr().CorElementType;
@@ -145,6 +115,14 @@ internal static Type InternalGetUnderlyingType(RuntimeType enumType)
145115
return GetEnumInfo(enumType).UnderlyingType;
146116
}
147117

118+
public static TEnum[] GetValues<TEnum>() where TEnum : struct, Enum
119+
{
120+
Array values = GetEnumInfo(typeof(TEnum)).ValuesAsUnderlyingType;
121+
TEnum[] result = new TEnum[values.Length];
122+
Array.Copy(values, result, values.Length);
123+
return result;
124+
}
125+
148126
//
149127
// Checks if value.GetType() matches enumType exactly.
150128
//

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/EnumInfo.cs

+62-24
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,84 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Diagnostics;
7-
using System.Numerics;
87
using System.Runtime;
98
using System.Runtime.CompilerServices;
109

1110
namespace System.Reflection
1211
{
1312
[ReflectionBlocked]
14-
public abstract class EnumInfo
13+
public sealed class EnumInfo
1514
{
16-
private protected EnumInfo(Type underlyingType, string[] names, bool isFlags)
15+
public EnumInfo(Type underlyingType, object[] rawValues, string[] names, bool isFlags)
1716
{
17+
Debug.Assert(rawValues.Length == names.Length);
18+
1819
UnderlyingType = underlyingType;
19-
Names = names;
20-
HasFlagsAttribute = isFlags;
21-
}
2220

23-
internal Type UnderlyingType { get; }
24-
internal string[] Names { get; }
25-
internal bool HasFlagsAttribute { get; }
26-
}
21+
int numValues = rawValues.Length;
22+
ulong[] values = new ulong[numValues];
23+
for (int i = 0; i < numValues; i++)
24+
{
25+
object rawValue = rawValues[i];
2726

28-
[ReflectionBlocked]
29-
public sealed class EnumInfo<TUnderlyingValue> : EnumInfo
30-
where TUnderlyingValue : struct, INumber<TUnderlyingValue>
31-
{
32-
public EnumInfo(Type underlyingType, TUnderlyingValue[] values, string[] names, bool isFlags) :
33-
base(underlyingType, names, isFlags)
34-
{
35-
Debug.Assert(values.Length == names.Length);
27+
ulong rawUnboxedValue;
28+
if (rawValue is ulong)
29+
{
30+
rawUnboxedValue = (ulong)rawValue;
31+
}
32+
else
33+
{
34+
// This conversion is this way for compatibility: do a value-preseving cast to long - then store (and compare) as ulong. This affects
35+
// the order in which the Enum apis return names and values.
36+
rawUnboxedValue = (ulong)(((IConvertible)rawValue).ToInt64(null));
37+
}
38+
values[i] = rawUnboxedValue;
39+
}
3640

37-
Array.Sort(keys: values, items: names);
41+
// Need to sort the `names` and `rawValues` arrays according to the `values` array
42+
ulong[] valuesCopy = (ulong[])values.Clone();
43+
Array.Sort(keys: valuesCopy, items: rawValues, comparer: Comparer<ulong>.Default);
44+
Array.Sort(keys: values, items: names, comparer: Comparer<ulong>.Default);
3845

46+
Names = names;
3947
Values = values;
40-
ValuesAreSequentialFromZero = Enum.AreSequentialFromZero(values);
48+
49+
// Create the unboxed version of values for the Values property to return. (We didn't do this earlier because
50+
// declaring "rawValues" as "Array" would prevent us from using the generic overload of Array.Sort()).
51+
//
52+
// The array element type is the underlying type, not the enum type. (The enum type could be an open generic.)
53+
ValuesAsUnderlyingType = Type.GetTypeCode(UnderlyingType) switch
54+
{
55+
TypeCode.Byte => new byte[numValues],
56+
TypeCode.SByte => new sbyte[numValues],
57+
TypeCode.UInt16 => new ushort[numValues],
58+
TypeCode.Int16 => new short[numValues],
59+
TypeCode.UInt32 => new uint[numValues],
60+
TypeCode.Int32 => new int[numValues],
61+
TypeCode.UInt64 => new ulong[numValues],
62+
TypeCode.Int64 => new long[numValues],
63+
_ => throw new NotSupportedException(),
64+
};
65+
Array.Copy(rawValues, ValuesAsUnderlyingType, numValues);
66+
67+
HasFlagsAttribute = isFlags;
68+
69+
ValuesAreSequentialFromZero = true;
70+
for (int i = 0; i < values.Length; i++)
71+
{
72+
if (values[i] != (ulong)i)
73+
{
74+
ValuesAreSequentialFromZero = false;
75+
break;
76+
}
77+
}
4178
}
4279

43-
internal TUnderlyingValue[] Values { get; }
80+
internal Type UnderlyingType { get; }
81+
internal string[] Names { get; }
82+
internal ulong[] Values { get; }
83+
internal Array ValuesAsUnderlyingType { get; }
84+
internal bool HasFlagsAttribute { get; }
4485
internal bool ValuesAreSequentialFromZero { get; }
45-
46-
public TUnderlyingValue[] CloneValues() =>
47-
new ReadOnlySpan<TUnderlyingValue>(Values).ToArray();
4886
}
4987
}

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -404,15 +404,15 @@ public sealed override void MakeTypedReference(object target, FieldInfo[] flds,
404404

405405
public sealed override Assembly[] GetLoadedAssemblies() => RuntimeAssemblyInfo.GetLoadedAssemblies();
406406

407-
public sealed override EnumInfo<TUnderlyingValue> GetEnumInfo<TUnderlyingValue>(Type type)
407+
public sealed override EnumInfo GetEnumInfo(Type type)
408408
{
409409
RuntimeTypeInfo runtimeType = type.CastToRuntimeTypeInfo();
410410

411-
EnumInfo<TUnderlyingValue>? info = runtimeType.GenericCache as EnumInfo<TUnderlyingValue>;
411+
EnumInfo? info = runtimeType.GenericCache as EnumInfo;
412412
if (info != null)
413413
return info;
414414

415-
info = ReflectionCoreExecution.ExecutionDomain.ExecutionEnvironment.GetEnumInfo<TUnderlyingValue>(runtimeType.TypeHandle);
415+
info = ReflectionCoreExecution.ExecutionDomain.ExecutionEnvironment.GetEnumInfo(runtimeType.TypeHandle);
416416
runtimeType.GenericCache = info;
417417
return info;
418418
}

src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs

+11-11
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,17 @@ public abstract class RuntimeType : TypeInfo
2222
// Do a value-preserving cast of both it and the enum values and do a 64-bit compare.
2323

2424
if (!IsActualEnum)
25-
throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");
25+
throw new ArgumentException(SR.Arg_MustBeEnum);
2626

27-
return Enum.GetName(this, rawValue);
27+
return Enum.GetEnumName(this, rawValue);
2828
}
2929

3030
public sealed override string[] GetEnumNames()
3131
{
3232
if (!IsActualEnum)
3333
throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");
3434

35-
string[] ret = Enum.GetNamesNoCopy(this);
35+
string[] ret = Enum.InternalGetNames(this);
3636

3737
// Make a copy since we can't hand out the same array since users can modify them
3838
return new ReadOnlySpan<string>(ret).ToArray();
@@ -87,7 +87,7 @@ public sealed override bool IsEnumDefined(object value)
8787
throw new ArgumentException(SR.Format(SR.Arg_EnumUnderlyingTypeAndObjectMustBeSameType, value.GetType(), underlyingType));
8888
}
8989

90-
return Enum.GetName(this, rawValue) != null;
90+
return Enum.GetEnumName(this, rawValue) != null;
9191
}
9292
}
9393

@@ -97,16 +97,16 @@ public sealed override Array GetEnumValues()
9797
if (!IsActualEnum)
9898
throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");
9999

100-
Array values = Enum.GetValuesAsUnderlyingTypeNoCopy(this);
100+
Array values = Enum.GetEnumInfo(this).ValuesAsUnderlyingType;
101101
int count = values.Length;
102-
103102
// Without universal shared generics, chances are slim that we'll have the appropriate
104103
// array type available. Offer an escape hatch that avoids a missing metadata exception
105104
// at the cost of a small appcompat risk.
106-
Array result = AppContext.TryGetSwitch("Switch.System.Enum.RelaxedGetValues", out bool isRelaxed) && isRelaxed ?
107-
Array.CreateInstance(Enum.InternalGetUnderlyingType(this), count) :
108-
Array.CreateInstance(this, count);
109-
105+
Array result;
106+
if (AppContext.TryGetSwitch("Switch.System.Enum.RelaxedGetValues", out bool isRelaxed) && isRelaxed)
107+
result = Array.CreateInstance(Enum.InternalGetUnderlyingType(this), count);
108+
else
109+
result = Array.CreateInstance(this, count);
110110
Array.Copy(values, result, values.Length);
111111
return result;
112112
}
@@ -116,7 +116,7 @@ public sealed override Array GetEnumValuesAsUnderlyingType()
116116
if (!IsActualEnum)
117117
throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");
118118

119-
return (Array)Enum.GetValuesAsUnderlyingTypeNoCopy(this).Clone();
119+
return (Array)Enum.GetEnumInfo(this).ValuesAsUnderlyingType.Clone();
120120
}
121121

122122
internal bool IsActualEnum

src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionCoreCallbacksImplementation.cs

+5-3
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ namespace Internal.Reflection
1212
{
1313
internal class ReflectionCoreCallbacksImplementation : ReflectionCoreCallbacks
1414
{
15-
public override EnumInfo<TUnderlyingValue> GetEnumInfo<TUnderlyingValue>(Type type) =>
16-
new EnumInfo<TUnderlyingValue>(
15+
public override EnumInfo GetEnumInfo(Type type)
16+
{
17+
return new EnumInfo(
1718
RuntimeAugments.GetEnumUnderlyingType(type.TypeHandle),
18-
values: Array.Empty<TUnderlyingValue>(),
19+
rawValues: Array.Empty<object>(),
1920
names: Array.Empty<string>(),
2021
isFlags: false);
22+
}
2123

2224
public override DynamicInvokeInfo GetDelegateDynamicInvokeInfo(Type type)
2325
=> throw new NotSupportedException(SR.Reflection_Disabled);

0 commit comments

Comments
 (0)