Skip to content

Commit 84150d9

Browse files
authored
Rewrite Enum.HasFlags and Enum.Equals in C# (#59514)
1 parent 2056905 commit 84150d9

File tree

10 files changed

+114
-184
lines changed

10 files changed

+114
-184
lines changed

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ public abstract partial class Enum
1313
[DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
1414
private static extern void GetEnumValuesAndNames(QCallTypeHandle enumType, ObjectHandleOnStack values, ObjectHandleOnStack names, Interop.BOOL getNames);
1515

16-
[MethodImpl(MethodImplOptions.InternalCall)]
17-
public extern override bool Equals([NotNullWhen(true)] object? obj);
18-
1916
[MethodImpl(MethodImplOptions.InternalCall)]
2017
private static extern object InternalBoxEnum(RuntimeType enumType, long value);
2118

@@ -25,9 +22,6 @@ public abstract partial class Enum
2522
[MethodImpl(MethodImplOptions.InternalCall)]
2623
internal static extern RuntimeType InternalGetUnderlyingType(RuntimeType enumType);
2724

28-
[MethodImpl(MethodImplOptions.InternalCall)]
29-
private extern bool InternalHasFlag(Enum flags);
30-
3125
private static EnumInfo GetEnumInfo(RuntimeType enumType, bool getNames = true)
3226
{
3327
EnumInfo? entry = enumType.GenericCache as EnumInfo;

src/coreclr/vm/commodule.cpp

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -902,38 +902,3 @@ FCIMPL1(FC_BOOL_RET, COMModule::IsResource, ReflectModuleBaseObject* pModuleUNSA
902902
FC_RETURN_BOOL(pModuleUNSAFE->GetModule()->IsResource());
903903
}
904904
FCIMPLEND
905-
906-
907-
//---------------------------------------------------------------------
908-
// Helper code for PunkSafeHandle class. This does the Release in the
909-
// safehandle's critical finalizer.
910-
//---------------------------------------------------------------------
911-
static VOID __stdcall DReleaseTarget(IUnknown *punk)
912-
{
913-
CONTRACTL
914-
{
915-
NOTHROW;
916-
GC_TRIGGERS;
917-
MODE_PREEMPTIVE;
918-
}
919-
CONTRACTL_END;
920-
921-
if (punk)
922-
{
923-
punk->Release();
924-
}
925-
}
926-
927-
928-
//---------------------------------------------------------------------
929-
// Helper code for PunkSafeHandle class. This returns the function that performs
930-
// the Release() for the safehandle's critical finalizer.
931-
//---------------------------------------------------------------------
932-
FCIMPL0(void*, COMPunkSafeHandle::nGetDReleaseTarget)
933-
{
934-
FCALL_CONTRACT;
935-
936-
return (void*)DReleaseTarget;
937-
}
938-
FCIMPLEND
939-

src/coreclr/vm/commodule.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,4 @@ class COMModule
110110

111111
};
112112

113-
class COMPunkSafeHandle
114-
{
115-
public:
116-
static FCDECL0(void*, nGetDReleaseTarget);
117-
};
118-
119113
#endif

src/coreclr/vm/ecalllist.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,8 @@ FCFuncStart(gEnumFuncs)
7979
FCFuncElement("InternalGetCorElementType", ReflectionEnum::InternalGetCorElementType)
8080
QCFuncElement("GetEnumValuesAndNames", ReflectionEnum::GetEnumValuesAndNames)
8181
FCFuncElement("InternalBoxEnum", ReflectionEnum::InternalBoxEnum)
82-
FCFuncElement("Equals", ReflectionEnum::InternalEquals)
83-
FCFuncElement("InternalHasFlag", ReflectionEnum::InternalHasFlag)
8482
FCFuncEnd()
8583

86-
87-
FCFuncStart(gSymWrapperCodePunkSafeHandleFuncs)
88-
FCFuncElement("nGetDReleaseTarget", COMPunkSafeHandle::nGetDReleaseTarget)
89-
FCFuncEnd()
90-
91-
9284
FCFuncStart(gObjectFuncs)
9385
FCIntrinsic("GetType", ObjectNative::GetClass, CORINFO_INTRINSIC_Object_GetType)
9486
FCFuncEnd()
@@ -1197,7 +1189,6 @@ FCClassElement("ObjectiveCMarshal", "System.Runtime.InteropServices.ObjectiveC",
11971189
FCClassElement("OverlappedData", "System.Threading", gOverlappedFuncs)
11981190

11991191

1200-
FCClassElement("PunkSafeHandle", "System.Reflection.Emit", gSymWrapperCodePunkSafeHandleFuncs)
12011192
FCClassElement("RegisteredWaitHandle", "System.Threading", gRegisteredWaitHandleFuncs)
12021193

12031194
FCClassElement("RuntimeAssembly", "System.Reflection", gRuntimeAssemblyFuncs)

src/coreclr/vm/reflectioninvocation.cpp

Lines changed: 0 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -2492,100 +2492,3 @@ FCIMPL2_IV(Object*, ReflectionEnum::InternalBoxEnum, ReflectClassBaseObject* tar
24922492
return OBJECTREFToObject(ret);
24932493
}
24942494
FCIMPLEND
2495-
2496-
//*************************************************************************************************
2497-
//*************************************************************************************************
2498-
//*************************************************************************************************
2499-
// ReflectionEnum
2500-
//*************************************************************************************************
2501-
//*************************************************************************************************
2502-
//*************************************************************************************************
2503-
2504-
FCIMPL2(FC_BOOL_RET, ReflectionEnum::InternalEquals, Object *pRefThis, Object* pRefTarget)
2505-
{
2506-
FCALL_CONTRACT;
2507-
2508-
VALIDATEOBJECT(pRefThis);
2509-
BOOL ret = false;
2510-
if (pRefTarget == NULL) {
2511-
FC_RETURN_BOOL(ret);
2512-
}
2513-
2514-
if( pRefThis == pRefTarget)
2515-
FC_RETURN_BOOL(true);
2516-
2517-
//Make sure we are comparing same type.
2518-
MethodTable* pMTThis = pRefThis->GetMethodTable();
2519-
_ASSERTE(!pMTThis->IsArray()); // bunch of assumptions about arrays wrong.
2520-
if ( pMTThis != pRefTarget->GetMethodTable()) {
2521-
FC_RETURN_BOOL(ret);
2522-
}
2523-
2524-
void * pThis = pRefThis->UnBox();
2525-
void * pTarget = pRefTarget->UnBox();
2526-
switch (pMTThis->GetNumInstanceFieldBytes()) {
2527-
case 1:
2528-
ret = (*(UINT8*)pThis == *(UINT8*)pTarget);
2529-
break;
2530-
case 2:
2531-
ret = (*(UINT16*)pThis == *(UINT16*)pTarget);
2532-
break;
2533-
case 4:
2534-
ret = (*(UINT32*)pThis == *(UINT32*)pTarget);
2535-
break;
2536-
case 8:
2537-
ret = (*(UINT64*)pThis == *(UINT64*)pTarget);
2538-
break;
2539-
default:
2540-
// should not reach here.
2541-
UNREACHABLE_MSG("Incorrect Enum Type size!");
2542-
break;
2543-
}
2544-
2545-
FC_RETURN_BOOL(ret);
2546-
}
2547-
FCIMPLEND
2548-
2549-
// perform (this & flags) == flags
2550-
FCIMPL2(FC_BOOL_RET, ReflectionEnum::InternalHasFlag, Object *pRefThis, Object* pRefFlags)
2551-
{
2552-
FCALL_CONTRACT;
2553-
2554-
VALIDATEOBJECT(pRefThis);
2555-
2556-
BOOL cmp = false;
2557-
2558-
_ASSERTE(pRefFlags != NULL); // Enum.cs would have thrown ArgumentNullException before calling into InternalHasFlag
2559-
2560-
VALIDATEOBJECT(pRefFlags);
2561-
2562-
void * pThis = pRefThis->UnBox();
2563-
void * pFlags = pRefFlags->UnBox();
2564-
2565-
MethodTable* pMTThis = pRefThis->GetMethodTable();
2566-
2567-
_ASSERTE(!pMTThis->IsArray()); // bunch of assumptions about arrays wrong.
2568-
_ASSERTE(pMTThis->GetNumInstanceFieldBytes() == pRefFlags->GetMethodTable()->GetNumInstanceFieldBytes()); // Enum.cs verifies that the types are Equivalent
2569-
2570-
switch (pMTThis->GetNumInstanceFieldBytes()) {
2571-
case 1:
2572-
cmp = ((*(UINT8*)pThis & *(UINT8*)pFlags) == *(UINT8*)pFlags);
2573-
break;
2574-
case 2:
2575-
cmp = ((*(UINT16*)pThis & *(UINT16*)pFlags) == *(UINT16*)pFlags);
2576-
break;
2577-
case 4:
2578-
cmp = ((*(UINT32*)pThis & *(UINT32*)pFlags) == *(UINT32*)pFlags);
2579-
break;
2580-
case 8:
2581-
cmp = ((*(UINT64*)pThis & *(UINT64*)pFlags) == *(UINT64*)pFlags);
2582-
break;
2583-
default:
2584-
// should not reach here.
2585-
UNREACHABLE_MSG("Incorrect Enum Type size!");
2586-
break;
2587-
}
2588-
2589-
FC_RETURN_BOOL(cmp);
2590-
}
2591-
FCIMPLEND

src/coreclr/vm/reflectioninvocation.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,6 @@ class ReflectionEnum {
9191
void QCALLTYPE GetEnumValuesAndNames(QCall::TypeHandle pEnumType, QCall::ObjectHandleOnStack pReturnValues, QCall::ObjectHandleOnStack pReturnNames, BOOL fGetNames);
9292

9393
static FCDECL2_IV(Object*, InternalBoxEnum, ReflectClassBaseObject* pEnumType, INT64 value);
94-
static FCDECL2(FC_BOOL_RET, InternalEquals, Object *pRefThis, Object* pRefTarget);
95-
static FCDECL2(FC_BOOL_RET, InternalHasFlag, Object *pRefThis, Object* pRefFlags);
9694
};
9795

9896
#endif // _REFLECTIONINVOCATION_H_

src/libraries/System.Private.CoreLib/src/System/Enum.cs

Lines changed: 114 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,6 @@
1010
using System.Runtime.InteropServices;
1111
using Internal.Runtime.CompilerServices;
1212

13-
#if CORERT
14-
using RuntimeType = System.Type;
15-
using EnumInfo = Internal.Runtime.Augments.EnumInfo;
16-
#endif
17-
1813
// The code below includes partial support for float/double and
1914
// pointer sized enums.
2015
//
@@ -340,10 +335,54 @@ public bool HasFlag(Enum flag)
340335
{
341336
if (flag is null)
342337
throw new ArgumentNullException(nameof(flag));
343-
if (!GetType().IsEquivalentTo(flag.GetType()))
338+
if (GetType() != flag.GetType() && !GetType().IsEquivalentTo(flag.GetType()))
344339
throw new ArgumentException(SR.Format(SR.Argument_EnumTypeDoesNotMatch, flag.GetType(), GetType()));
345340

346-
return InternalHasFlag(flag);
341+
ref byte pThisValue = ref this.GetRawData();
342+
ref byte pFlagsValue = ref flag.GetRawData();
343+
344+
switch (InternalGetCorElementType())
345+
{
346+
case CorElementType.ELEMENT_TYPE_I1:
347+
case CorElementType.ELEMENT_TYPE_U1:
348+
case CorElementType.ELEMENT_TYPE_BOOLEAN:
349+
{
350+
byte flagsValue = pFlagsValue;
351+
return (pThisValue & flagsValue) == flagsValue;
352+
}
353+
case CorElementType.ELEMENT_TYPE_I2:
354+
case CorElementType.ELEMENT_TYPE_U2:
355+
case CorElementType.ELEMENT_TYPE_CHAR:
356+
{
357+
ushort flagsValue = Unsafe.As<byte, ushort>(ref pFlagsValue);
358+
return (Unsafe.As<byte, ushort>(ref pThisValue) & flagsValue) == flagsValue;
359+
}
360+
case CorElementType.ELEMENT_TYPE_I4:
361+
case CorElementType.ELEMENT_TYPE_U4:
362+
#if TARGET_32BIT
363+
case CorElementType.ELEMENT_TYPE_I:
364+
case CorElementType.ELEMENT_TYPE_U:
365+
#endif
366+
case CorElementType.ELEMENT_TYPE_R4:
367+
{
368+
uint flagsValue = Unsafe.As<byte, uint>(ref pFlagsValue);
369+
return (Unsafe.As<byte, uint>(ref pThisValue) & flagsValue) == flagsValue;
370+
}
371+
case CorElementType.ELEMENT_TYPE_I8:
372+
case CorElementType.ELEMENT_TYPE_U8:
373+
#if TARGET_64BIT
374+
case CorElementType.ELEMENT_TYPE_I:
375+
case CorElementType.ELEMENT_TYPE_U:
376+
#endif
377+
case CorElementType.ELEMENT_TYPE_R8:
378+
{
379+
ulong flagsValue = Unsafe.As<byte, ulong>(ref pFlagsValue);
380+
return (Unsafe.As<byte, ulong>(ref pThisValue) & flagsValue) == flagsValue;
381+
}
382+
default:
383+
Debug.Fail("Unknown enum underlying type");
384+
return false;
385+
}
347386
}
348387

349388
internal static ulong[] InternalGetValues(RuntimeType enumType)
@@ -1103,28 +1142,83 @@ private ulong ToUInt64()
11031142
case CorElementType.ELEMENT_TYPE_CHAR:
11041143
return Unsafe.As<byte, ushort>(ref data);
11051144
case CorElementType.ELEMENT_TYPE_I4:
1145+
#if TARGET_32BIT
1146+
case CorElementType.ELEMENT_TYPE_I:
1147+
#endif
11061148
return (ulong)Unsafe.As<byte, int>(ref data);
11071149
case CorElementType.ELEMENT_TYPE_U4:
1150+
#if TARGET_32BIT
1151+
case CorElementType.ELEMENT_TYPE_U:
1152+
#endif
11081153
case CorElementType.ELEMENT_TYPE_R4:
11091154
return Unsafe.As<byte, uint>(ref data);
11101155
case CorElementType.ELEMENT_TYPE_I8:
1156+
#if TARGET_64BIT
1157+
case CorElementType.ELEMENT_TYPE_I:
1158+
#endif
11111159
return (ulong)Unsafe.As<byte, long>(ref data);
11121160
case CorElementType.ELEMENT_TYPE_U8:
1161+
#if TARGET_64BIT
1162+
case CorElementType.ELEMENT_TYPE_U:
1163+
#endif
11131164
case CorElementType.ELEMENT_TYPE_R8:
11141165
return Unsafe.As<byte, ulong>(ref data);
1115-
case CorElementType.ELEMENT_TYPE_I:
1116-
return (ulong)Unsafe.As<byte, IntPtr>(ref data);
1117-
case CorElementType.ELEMENT_TYPE_U:
1118-
return (ulong)Unsafe.As<byte, UIntPtr>(ref data);
11191166
default:
1120-
throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
1167+
Debug.Fail("Unknown enum underlying type");
1168+
return 0;
11211169
}
11221170
}
11231171

11241172
#endregion
11251173

11261174
#region Object Overrides
11271175

1176+
public override bool Equals([NotNullWhen(true)] object? obj)
1177+
{
1178+
if (obj is null)
1179+
return false;
1180+
1181+
if (this == obj)
1182+
return true;
1183+
1184+
if (this.GetType() != obj.GetType())
1185+
return false;
1186+
1187+
ref byte pThisValue = ref this.GetRawData();
1188+
ref byte pOtherValue = ref obj.GetRawData();
1189+
1190+
switch (InternalGetCorElementType())
1191+
{
1192+
case CorElementType.ELEMENT_TYPE_I1:
1193+
case CorElementType.ELEMENT_TYPE_U1:
1194+
case CorElementType.ELEMENT_TYPE_BOOLEAN:
1195+
return pThisValue == pOtherValue;
1196+
case CorElementType.ELEMENT_TYPE_I2:
1197+
case CorElementType.ELEMENT_TYPE_U2:
1198+
case CorElementType.ELEMENT_TYPE_CHAR:
1199+
return Unsafe.As<byte, ushort>(ref pThisValue) == Unsafe.As<byte, ushort>(ref pOtherValue);
1200+
case CorElementType.ELEMENT_TYPE_I4:
1201+
case CorElementType.ELEMENT_TYPE_U4:
1202+
#if TARGET_32BIT
1203+
case CorElementType.ELEMENT_TYPE_I:
1204+
case CorElementType.ELEMENT_TYPE_U:
1205+
#endif
1206+
case CorElementType.ELEMENT_TYPE_R4:
1207+
return Unsafe.As<byte, uint>(ref pThisValue) == Unsafe.As<byte, uint>(ref pOtherValue);
1208+
case CorElementType.ELEMENT_TYPE_I8:
1209+
case CorElementType.ELEMENT_TYPE_U8:
1210+
#if TARGET_64BIT
1211+
case CorElementType.ELEMENT_TYPE_I:
1212+
case CorElementType.ELEMENT_TYPE_U:
1213+
#endif
1214+
case CorElementType.ELEMENT_TYPE_R8:
1215+
return Unsafe.As<byte, ulong>(ref pThisValue) == Unsafe.As<byte, ulong>(ref pOtherValue);
1216+
default:
1217+
Debug.Fail("Unknown enum underlying type");
1218+
return false;
1219+
}
1220+
}
1221+
11281222
public override int GetHashCode()
11291223
{
11301224
// CONTRACT with the runtime: GetHashCode of enum types is implemented as GetHashCode of the underlying type.
@@ -1214,7 +1308,8 @@ public int CompareTo(object? target)
12141308
case CorElementType.ELEMENT_TYPE_R8:
12151309
return Unsafe.As<byte, double>(ref pThisValue).CompareTo(Unsafe.As<byte, double>(ref pTargetValue));
12161310
default:
1217-
throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
1311+
Debug.Fail("Unknown enum underlying type");
1312+
return 0;
12181313
}
12191314
}
12201315
#endregion
@@ -1408,6 +1503,12 @@ private static RuntimeType ValidateRuntimeType(Type enumType)
14081503
throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
14091504
if (enumType is not RuntimeType rtType)
14101505
throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1506+
#if CORERT
1507+
// Check for the unfortunate "typeof(Outer<>).InnerEnum" corner case.
1508+
// https://github.com/dotnet/runtime/issues/7976
1509+
if (enumType.ContainsGenericParameters)
1510+
throw new InvalidOperationException(SR.Format(SR.Arg_OpenType, enumType.ToString()));
1511+
#endif
14111512
return rtType;
14121513
}
14131514
}

src/mono/System.Private.CoreLib/src/System/Enum.Mono.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ public partial class Enum
2020
[MethodImpl(MethodImplOptions.InternalCall)]
2121
internal static extern RuntimeType InternalGetUnderlyingType(RuntimeType enumType);
2222

23-
[MethodImpl(MethodImplOptions.InternalCall)]
24-
private extern bool InternalHasFlag(Enum flags);
25-
2623
private static EnumInfo GetEnumInfo(RuntimeType enumType, bool getNames = true)
2724
{
2825
EnumInfo? entry = enumType.Cache.EnumInfo;

0 commit comments

Comments
 (0)