Skip to content

Commit

Permalink
Revert "Rewrite Enum and add {ISpanFormattable}.TryFormat (#78580)"
Browse files Browse the repository at this point in the history
This reverts commit 62f3eb2.
  • Loading branch information
stephentoub authored Dec 6, 2022
1 parent c4b37da commit a938ea8
Show file tree
Hide file tree
Showing 38 changed files with 1,357 additions and 2,363 deletions.
30 changes: 8 additions & 22 deletions src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Numerics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
Expand All @@ -20,15 +20,6 @@ public abstract partial class Enum
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern unsafe CorElementType InternalGetCorElementType(MethodTable* pMT);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe CorElementType InternalGetCorElementType(RuntimeType rt)
{
Debug.Assert(rt.IsActualEnum);
CorElementType elementType = InternalGetCorElementType((MethodTable*)rt.GetUnderlyingNativeHandle());
GC.KeepAlive(rt);
return elementType;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe CorElementType InternalGetCorElementType()
{
Expand Down Expand Up @@ -82,31 +73,26 @@ internal static unsafe RuntimeType InternalGetUnderlyingType(RuntimeType enumTyp
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static EnumInfo<TUnderlyingValue> GetEnumInfo<TUnderlyingValue>(RuntimeType enumType, bool getNames = true)
where TUnderlyingValue : struct, INumber<TUnderlyingValue>
private static EnumInfo GetEnumInfo(RuntimeType enumType, bool getNames = true)
{
return enumType.GenericCache is EnumInfo<TUnderlyingValue> info && (!getNames || info.Names is not null) ?
return enumType.GenericCache is EnumInfo info && (!getNames || info.Names is not null) ?
info :
InitializeEnumInfo(enumType, getNames);

[MethodImpl(MethodImplOptions.NoInlining)]
static EnumInfo<TUnderlyingValue> InitializeEnumInfo(RuntimeType enumType, bool getNames)
static EnumInfo InitializeEnumInfo(RuntimeType enumType, bool getNames)
{
TUnderlyingValue[]? values = null;
ulong[]? values = null;
string[]? names = null;

RuntimeTypeHandle enumTypeHandle = enumType.TypeHandle;
GetEnumValuesAndNames(
new QCallTypeHandle(ref enumType),
new QCallTypeHandle(ref enumTypeHandle),
ObjectHandleOnStack.Create(ref values),
ObjectHandleOnStack.Create(ref names),
getNames ? Interop.BOOL.TRUE : Interop.BOOL.FALSE);

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<TUnderlyingValue>(hasFlagsAttribute, values, names!);
var entry = new EnumInfo(hasFlagsAttribute, values!, names!);
enumType.GenericCache = entry;
return entry;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -824,10 +824,6 @@
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:System.Reflection.EnumInfo</Target>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:System.Reflection.EnumInfo`1</Target>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:System.Reflection.Runtime.General.MetadataReaderExtensions</Target>
Expand Down Expand Up @@ -976,4 +972,4 @@
<DiagnosticId>CP0016</DiagnosticId>
<Target>M:System.Runtime.InteropServices.Marshal.GetObjectForNativeVariant``1(System.IntPtr)-&gt;T?:[T:System.Diagnostics.CodeAnalysis.MaybeNullAttribute]</Target>
</Suppression>
</Suppressions>
</Suppressions>
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Numerics;

using EETypeElementType = Internal.Runtime.EETypeElementType;

Expand Down Expand Up @@ -165,8 +164,7 @@ public abstract object ActivatorCreateInstance(

public abstract Assembly[] GetLoadedAssemblies();

public abstract EnumInfo<TUnderlyingValue> GetEnumInfo<TUnderlyingValue>(Type type)
where TUnderlyingValue : struct, INumber<TUnderlyingValue>;
public abstract EnumInfo GetEnumInfo(Type type);

public abstract DynamicInvokeInfo GetDelegateDynamicInvokeInfo(Type type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Reflection;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Reflection.Runtime.General;
using System.Reflection.Runtime.TypeInfos;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -98,9 +97,7 @@ public abstract class ExecutionEnvironment
// Other
//==============================================================================================
public abstract FieldAccessor CreateLiteralFieldAccessor(object value, RuntimeTypeHandle fieldTypeHandle);
public abstract EnumInfo<TUnderlyingValue> GetEnumInfo<TUnderlyingValue>(RuntimeTypeHandle typeHandle)
where TUnderlyingValue : struct, INumber<TUnderlyingValue>;

public abstract EnumInfo GetEnumInfo(RuntimeTypeHandle typeHandle);
public abstract IntPtr GetDynamicInvokeThunk(MethodInvoker invoker);

//==============================================================================================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Reflection;
using System.Runtime;
using System.Runtime.CompilerServices;
using Internal.Runtime.Augments;
using Internal.Runtime.CompilerServices;
using Internal.Reflection.Augments;

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

RuntimeType rt = (RuntimeType)enumType;
return Type.GetTypeCode(RuntimeAugments.GetEnumUnderlyingType(rt.TypeHandle)) switch
{
TypeCode.SByte => GetEnumInfo<sbyte>(rt),
TypeCode.Byte => GetEnumInfo<byte>(rt),
TypeCode.Int16 => GetEnumInfo<short>(rt),
TypeCode.UInt16 => GetEnumInfo<ushort>(rt),
TypeCode.Int32 => GetEnumInfo<int>(rt),
TypeCode.UInt32 => GetEnumInfo<uint>(rt),
TypeCode.Int64 => GetEnumInfo<long>(rt),
TypeCode.UInt64 => GetEnumInfo<ulong>(rt),
_ => throw new NotSupportedException(),
};
}

internal static EnumInfo<TUnderlyingValue> GetEnumInfo<TUnderlyingValue>(Type enumType, bool getNames = true)
where TUnderlyingValue : struct, INumber<TUnderlyingValue>
{
Debug.Assert(enumType != null);
Debug.Assert(enumType is RuntimeType);
Debug.Assert(enumType.IsEnum);

return ReflectionAugments.ReflectionCoreCallbacks.GetEnumInfo<TUnderlyingValue>(enumType);
return ReflectionAugments.ReflectionCoreCallbacks.GetEnumInfo(enumType);
}
#pragma warning restore

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

private static CorElementType InternalGetCorElementType(RuntimeType rt)
{
Debug.Assert(rt.IsActualEnum);
return rt.TypeHandle.ToEETypePtr().CorElementType;
}

private CorElementType InternalGetCorElementType()
{
return this.GetEETypePtr().CorElementType;
Expand Down Expand Up @@ -145,6 +115,14 @@ internal static Type InternalGetUnderlyingType(RuntimeType enumType)
return GetEnumInfo(enumType).UnderlyingType;
}

public static TEnum[] GetValues<TEnum>() where TEnum : struct, Enum
{
Array values = GetEnumInfo(typeof(TEnum)).ValuesAsUnderlyingType;
TEnum[] result = new TEnum[values.Length];
Array.Copy(values, result, values.Length);
return result;
}

//
// Checks if value.GetType() matches enumType exactly.
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,84 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;
using System.Runtime;
using System.Runtime.CompilerServices;

namespace System.Reflection
{
[ReflectionBlocked]
public abstract class EnumInfo
public sealed class EnumInfo
{
private protected EnumInfo(Type underlyingType, string[] names, bool isFlags)
public EnumInfo(Type underlyingType, object[] rawValues, string[] names, bool isFlags)
{
Debug.Assert(rawValues.Length == names.Length);

UnderlyingType = underlyingType;
Names = names;
HasFlagsAttribute = isFlags;
}

internal Type UnderlyingType { get; }
internal string[] Names { get; }
internal bool HasFlagsAttribute { get; }
}
int numValues = rawValues.Length;
ulong[] values = new ulong[numValues];
for (int i = 0; i < numValues; i++)
{
object rawValue = rawValues[i];

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

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

Names = names;
Values = values;
ValuesAreSequentialFromZero = Enum.AreSequentialFromZero(values);

// Create the unboxed version of values for the Values property to return. (We didn't do this earlier because
// declaring "rawValues" as "Array" would prevent us from using the generic overload of Array.Sort()).
//
// The array element type is the underlying type, not the enum type. (The enum type could be an open generic.)
ValuesAsUnderlyingType = Type.GetTypeCode(UnderlyingType) 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(),
};
Array.Copy(rawValues, ValuesAsUnderlyingType, numValues);

HasFlagsAttribute = isFlags;

ValuesAreSequentialFromZero = true;
for (int i = 0; i < values.Length; i++)
{
if (values[i] != (ulong)i)
{
ValuesAreSequentialFromZero = false;
break;
}
}
}

internal TUnderlyingValue[] Values { get; }
internal Type UnderlyingType { get; }
internal string[] Names { get; }
internal ulong[] Values { get; }
internal Array ValuesAsUnderlyingType { get; }
internal bool HasFlagsAttribute { get; }
internal bool ValuesAreSequentialFromZero { get; }

public TUnderlyingValue[] CloneValues() =>
new ReadOnlySpan<TUnderlyingValue>(Values).ToArray();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -404,15 +404,15 @@ public sealed override void MakeTypedReference(object target, FieldInfo[] flds,

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

public sealed override EnumInfo<TUnderlyingValue> GetEnumInfo<TUnderlyingValue>(Type type)
public sealed override EnumInfo GetEnumInfo(Type type)
{
RuntimeTypeInfo runtimeType = type.CastToRuntimeTypeInfo();

EnumInfo<TUnderlyingValue>? info = runtimeType.GenericCache as EnumInfo<TUnderlyingValue>;
EnumInfo? info = runtimeType.GenericCache as EnumInfo;
if (info != null)
return info;

info = ReflectionCoreExecution.ExecutionDomain.ExecutionEnvironment.GetEnumInfo<TUnderlyingValue>(runtimeType.TypeHandle);
info = ReflectionCoreExecution.ExecutionDomain.ExecutionEnvironment.GetEnumInfo(runtimeType.TypeHandle);
runtimeType.GenericCache = info;
return info;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ public abstract class RuntimeType : TypeInfo
// Do a value-preserving cast of both it and the enum values and do a 64-bit compare.

if (!IsActualEnum)
throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");
throw new ArgumentException(SR.Arg_MustBeEnum);

return Enum.GetName(this, rawValue);
return Enum.GetEnumName(this, rawValue);
}

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

string[] ret = Enum.GetNamesNoCopy(this);
string[] ret = Enum.InternalGetNames(this);

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

return Enum.GetName(this, rawValue) != null;
return Enum.GetEnumName(this, rawValue) != null;
}
}

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

Array values = Enum.GetValuesAsUnderlyingTypeNoCopy(this);
Array values = Enum.GetEnumInfo(this).ValuesAsUnderlyingType;
int count = values.Length;

// Without universal shared generics, chances are slim that we'll have the appropriate
// array type available. Offer an escape hatch that avoids a missing metadata exception
// at the cost of a small appcompat risk.
Array result = AppContext.TryGetSwitch("Switch.System.Enum.RelaxedGetValues", out bool isRelaxed) && isRelaxed ?
Array.CreateInstance(Enum.InternalGetUnderlyingType(this), count) :
Array.CreateInstance(this, count);

Array result;
if (AppContext.TryGetSwitch("Switch.System.Enum.RelaxedGetValues", out bool isRelaxed) && isRelaxed)
result = Array.CreateInstance(Enum.InternalGetUnderlyingType(this), count);
else
result = Array.CreateInstance(this, count);
Array.Copy(values, result, values.Length);
return result;
}
Expand All @@ -116,7 +116,7 @@ public sealed override Array GetEnumValuesAsUnderlyingType()
if (!IsActualEnum)
throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");

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

internal bool IsActualEnum
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ namespace Internal.Reflection
{
internal class ReflectionCoreCallbacksImplementation : ReflectionCoreCallbacks
{
public override EnumInfo<TUnderlyingValue> GetEnumInfo<TUnderlyingValue>(Type type) =>
new EnumInfo<TUnderlyingValue>(
public override EnumInfo GetEnumInfo(Type type)
{
return new EnumInfo(
RuntimeAugments.GetEnumUnderlyingType(type.TypeHandle),
values: Array.Empty<TUnderlyingValue>(),
rawValues: Array.Empty<object>(),
names: Array.Empty<string>(),
isFlags: false);
}

public override DynamicInvokeInfo GetDelegateDynamicInvokeInfo(Type type)
=> throw new NotSupportedException(SR.Reflection_Disabled);
Expand Down
Loading

0 comments on commit a938ea8

Please sign in to comment.