Skip to content

Commit

Permalink
Rewrite Enum.HasFlags and Enum.Equals in C# (#59514)
Browse files Browse the repository at this point in the history
  • Loading branch information
jkotas authored Sep 24, 2021
1 parent 2056905 commit 84150d9
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 184 deletions.
6 changes: 0 additions & 6 deletions src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ public abstract partial class Enum
[DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern void GetEnumValuesAndNames(QCallTypeHandle enumType, ObjectHandleOnStack values, ObjectHandleOnStack names, Interop.BOOL getNames);

[MethodImpl(MethodImplOptions.InternalCall)]
public extern override bool Equals([NotNullWhen(true)] object? obj);

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object InternalBoxEnum(RuntimeType enumType, long value);

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

[MethodImpl(MethodImplOptions.InternalCall)]
private extern bool InternalHasFlag(Enum flags);

private static EnumInfo GetEnumInfo(RuntimeType enumType, bool getNames = true)
{
EnumInfo? entry = enumType.GenericCache as EnumInfo;
Expand Down
35 changes: 0 additions & 35 deletions src/coreclr/vm/commodule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -902,38 +902,3 @@ FCIMPL1(FC_BOOL_RET, COMModule::IsResource, ReflectModuleBaseObject* pModuleUNSA
FC_RETURN_BOOL(pModuleUNSAFE->GetModule()->IsResource());
}
FCIMPLEND


//---------------------------------------------------------------------
// Helper code for PunkSafeHandle class. This does the Release in the
// safehandle's critical finalizer.
//---------------------------------------------------------------------
static VOID __stdcall DReleaseTarget(IUnknown *punk)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_PREEMPTIVE;
}
CONTRACTL_END;

if (punk)
{
punk->Release();
}
}


//---------------------------------------------------------------------
// Helper code for PunkSafeHandle class. This returns the function that performs
// the Release() for the safehandle's critical finalizer.
//---------------------------------------------------------------------
FCIMPL0(void*, COMPunkSafeHandle::nGetDReleaseTarget)
{
FCALL_CONTRACT;

return (void*)DReleaseTarget;
}
FCIMPLEND

6 changes: 0 additions & 6 deletions src/coreclr/vm/commodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,4 @@ class COMModule

};

class COMPunkSafeHandle
{
public:
static FCDECL0(void*, nGetDReleaseTarget);
};

#endif
9 changes: 0 additions & 9 deletions src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,8 @@ FCFuncStart(gEnumFuncs)
FCFuncElement("InternalGetCorElementType", ReflectionEnum::InternalGetCorElementType)
QCFuncElement("GetEnumValuesAndNames", ReflectionEnum::GetEnumValuesAndNames)
FCFuncElement("InternalBoxEnum", ReflectionEnum::InternalBoxEnum)
FCFuncElement("Equals", ReflectionEnum::InternalEquals)
FCFuncElement("InternalHasFlag", ReflectionEnum::InternalHasFlag)
FCFuncEnd()


FCFuncStart(gSymWrapperCodePunkSafeHandleFuncs)
FCFuncElement("nGetDReleaseTarget", COMPunkSafeHandle::nGetDReleaseTarget)
FCFuncEnd()


FCFuncStart(gObjectFuncs)
FCIntrinsic("GetType", ObjectNative::GetClass, CORINFO_INTRINSIC_Object_GetType)
FCFuncEnd()
Expand Down Expand Up @@ -1197,7 +1189,6 @@ FCClassElement("ObjectiveCMarshal", "System.Runtime.InteropServices.ObjectiveC",
FCClassElement("OverlappedData", "System.Threading", gOverlappedFuncs)


FCClassElement("PunkSafeHandle", "System.Reflection.Emit", gSymWrapperCodePunkSafeHandleFuncs)
FCClassElement("RegisteredWaitHandle", "System.Threading", gRegisteredWaitHandleFuncs)

FCClassElement("RuntimeAssembly", "System.Reflection", gRuntimeAssemblyFuncs)
Expand Down
97 changes: 0 additions & 97 deletions src/coreclr/vm/reflectioninvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2492,100 +2492,3 @@ FCIMPL2_IV(Object*, ReflectionEnum::InternalBoxEnum, ReflectClassBaseObject* tar
return OBJECTREFToObject(ret);
}
FCIMPLEND

//*************************************************************************************************
//*************************************************************************************************
//*************************************************************************************************
// ReflectionEnum
//*************************************************************************************************
//*************************************************************************************************
//*************************************************************************************************

FCIMPL2(FC_BOOL_RET, ReflectionEnum::InternalEquals, Object *pRefThis, Object* pRefTarget)
{
FCALL_CONTRACT;

VALIDATEOBJECT(pRefThis);
BOOL ret = false;
if (pRefTarget == NULL) {
FC_RETURN_BOOL(ret);
}

if( pRefThis == pRefTarget)
FC_RETURN_BOOL(true);

//Make sure we are comparing same type.
MethodTable* pMTThis = pRefThis->GetMethodTable();
_ASSERTE(!pMTThis->IsArray()); // bunch of assumptions about arrays wrong.
if ( pMTThis != pRefTarget->GetMethodTable()) {
FC_RETURN_BOOL(ret);
}

void * pThis = pRefThis->UnBox();
void * pTarget = pRefTarget->UnBox();
switch (pMTThis->GetNumInstanceFieldBytes()) {
case 1:
ret = (*(UINT8*)pThis == *(UINT8*)pTarget);
break;
case 2:
ret = (*(UINT16*)pThis == *(UINT16*)pTarget);
break;
case 4:
ret = (*(UINT32*)pThis == *(UINT32*)pTarget);
break;
case 8:
ret = (*(UINT64*)pThis == *(UINT64*)pTarget);
break;
default:
// should not reach here.
UNREACHABLE_MSG("Incorrect Enum Type size!");
break;
}

FC_RETURN_BOOL(ret);
}
FCIMPLEND

// perform (this & flags) == flags
FCIMPL2(FC_BOOL_RET, ReflectionEnum::InternalHasFlag, Object *pRefThis, Object* pRefFlags)
{
FCALL_CONTRACT;

VALIDATEOBJECT(pRefThis);

BOOL cmp = false;

_ASSERTE(pRefFlags != NULL); // Enum.cs would have thrown ArgumentNullException before calling into InternalHasFlag

VALIDATEOBJECT(pRefFlags);

void * pThis = pRefThis->UnBox();
void * pFlags = pRefFlags->UnBox();

MethodTable* pMTThis = pRefThis->GetMethodTable();

_ASSERTE(!pMTThis->IsArray()); // bunch of assumptions about arrays wrong.
_ASSERTE(pMTThis->GetNumInstanceFieldBytes() == pRefFlags->GetMethodTable()->GetNumInstanceFieldBytes()); // Enum.cs verifies that the types are Equivalent

switch (pMTThis->GetNumInstanceFieldBytes()) {
case 1:
cmp = ((*(UINT8*)pThis & *(UINT8*)pFlags) == *(UINT8*)pFlags);
break;
case 2:
cmp = ((*(UINT16*)pThis & *(UINT16*)pFlags) == *(UINT16*)pFlags);
break;
case 4:
cmp = ((*(UINT32*)pThis & *(UINT32*)pFlags) == *(UINT32*)pFlags);
break;
case 8:
cmp = ((*(UINT64*)pThis & *(UINT64*)pFlags) == *(UINT64*)pFlags);
break;
default:
// should not reach here.
UNREACHABLE_MSG("Incorrect Enum Type size!");
break;
}

FC_RETURN_BOOL(cmp);
}
FCIMPLEND
2 changes: 0 additions & 2 deletions src/coreclr/vm/reflectioninvocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,6 @@ class ReflectionEnum {
void QCALLTYPE GetEnumValuesAndNames(QCall::TypeHandle pEnumType, QCall::ObjectHandleOnStack pReturnValues, QCall::ObjectHandleOnStack pReturnNames, BOOL fGetNames);

static FCDECL2_IV(Object*, InternalBoxEnum, ReflectClassBaseObject* pEnumType, INT64 value);
static FCDECL2(FC_BOOL_RET, InternalEquals, Object *pRefThis, Object* pRefTarget);
static FCDECL2(FC_BOOL_RET, InternalHasFlag, Object *pRefThis, Object* pRefFlags);
};

#endif // _REFLECTIONINVOCATION_H_
127 changes: 114 additions & 13 deletions src/libraries/System.Private.CoreLib/src/System/Enum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@
using System.Runtime.InteropServices;
using Internal.Runtime.CompilerServices;

#if CORERT
using RuntimeType = System.Type;
using EnumInfo = Internal.Runtime.Augments.EnumInfo;
#endif

// The code below includes partial support for float/double and
// pointer sized enums.
//
Expand Down Expand Up @@ -340,10 +335,54 @@ public bool HasFlag(Enum flag)
{
if (flag is null)
throw new ArgumentNullException(nameof(flag));
if (!GetType().IsEquivalentTo(flag.GetType()))
if (GetType() != flag.GetType() && !GetType().IsEquivalentTo(flag.GetType()))
throw new ArgumentException(SR.Format(SR.Argument_EnumTypeDoesNotMatch, flag.GetType(), GetType()));

return InternalHasFlag(flag);
ref byte pThisValue = ref this.GetRawData();
ref byte pFlagsValue = ref flag.GetRawData();

switch (InternalGetCorElementType())
{
case CorElementType.ELEMENT_TYPE_I1:
case CorElementType.ELEMENT_TYPE_U1:
case CorElementType.ELEMENT_TYPE_BOOLEAN:
{
byte flagsValue = pFlagsValue;
return (pThisValue & flagsValue) == flagsValue;
}
case CorElementType.ELEMENT_TYPE_I2:
case CorElementType.ELEMENT_TYPE_U2:
case CorElementType.ELEMENT_TYPE_CHAR:
{
ushort flagsValue = Unsafe.As<byte, ushort>(ref pFlagsValue);
return (Unsafe.As<byte, ushort>(ref pThisValue) & flagsValue) == flagsValue;
}
case CorElementType.ELEMENT_TYPE_I4:
case CorElementType.ELEMENT_TYPE_U4:
#if TARGET_32BIT
case CorElementType.ELEMENT_TYPE_I:
case CorElementType.ELEMENT_TYPE_U:
#endif
case CorElementType.ELEMENT_TYPE_R4:
{
uint flagsValue = Unsafe.As<byte, uint>(ref pFlagsValue);
return (Unsafe.As<byte, uint>(ref pThisValue) & flagsValue) == flagsValue;
}
case CorElementType.ELEMENT_TYPE_I8:
case CorElementType.ELEMENT_TYPE_U8:
#if TARGET_64BIT
case CorElementType.ELEMENT_TYPE_I:
case CorElementType.ELEMENT_TYPE_U:
#endif
case CorElementType.ELEMENT_TYPE_R8:
{
ulong flagsValue = Unsafe.As<byte, ulong>(ref pFlagsValue);
return (Unsafe.As<byte, ulong>(ref pThisValue) & flagsValue) == flagsValue;
}
default:
Debug.Fail("Unknown enum underlying type");
return false;
}
}

internal static ulong[] InternalGetValues(RuntimeType enumType)
Expand Down Expand Up @@ -1103,28 +1142,83 @@ private ulong ToUInt64()
case CorElementType.ELEMENT_TYPE_CHAR:
return Unsafe.As<byte, ushort>(ref data);
case CorElementType.ELEMENT_TYPE_I4:
#if TARGET_32BIT
case CorElementType.ELEMENT_TYPE_I:
#endif
return (ulong)Unsafe.As<byte, int>(ref data);
case CorElementType.ELEMENT_TYPE_U4:
#if TARGET_32BIT
case CorElementType.ELEMENT_TYPE_U:
#endif
case CorElementType.ELEMENT_TYPE_R4:
return Unsafe.As<byte, uint>(ref data);
case CorElementType.ELEMENT_TYPE_I8:
#if TARGET_64BIT
case CorElementType.ELEMENT_TYPE_I:
#endif
return (ulong)Unsafe.As<byte, long>(ref data);
case CorElementType.ELEMENT_TYPE_U8:
#if TARGET_64BIT
case CorElementType.ELEMENT_TYPE_U:
#endif
case CorElementType.ELEMENT_TYPE_R8:
return Unsafe.As<byte, ulong>(ref data);
case CorElementType.ELEMENT_TYPE_I:
return (ulong)Unsafe.As<byte, IntPtr>(ref data);
case CorElementType.ELEMENT_TYPE_U:
return (ulong)Unsafe.As<byte, UIntPtr>(ref data);
default:
throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
Debug.Fail("Unknown enum underlying type");
return 0;
}
}

#endregion

#region Object Overrides

public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj is null)
return false;

if (this == obj)
return true;

if (this.GetType() != obj.GetType())
return false;

ref byte pThisValue = ref this.GetRawData();
ref byte pOtherValue = ref obj.GetRawData();

switch (InternalGetCorElementType())
{
case CorElementType.ELEMENT_TYPE_I1:
case CorElementType.ELEMENT_TYPE_U1:
case CorElementType.ELEMENT_TYPE_BOOLEAN:
return pThisValue == pOtherValue;
case CorElementType.ELEMENT_TYPE_I2:
case CorElementType.ELEMENT_TYPE_U2:
case CorElementType.ELEMENT_TYPE_CHAR:
return Unsafe.As<byte, ushort>(ref pThisValue) == Unsafe.As<byte, ushort>(ref pOtherValue);
case CorElementType.ELEMENT_TYPE_I4:
case CorElementType.ELEMENT_TYPE_U4:
#if TARGET_32BIT
case CorElementType.ELEMENT_TYPE_I:
case CorElementType.ELEMENT_TYPE_U:
#endif
case CorElementType.ELEMENT_TYPE_R4:
return Unsafe.As<byte, uint>(ref pThisValue) == Unsafe.As<byte, uint>(ref pOtherValue);
case CorElementType.ELEMENT_TYPE_I8:
case CorElementType.ELEMENT_TYPE_U8:
#if TARGET_64BIT
case CorElementType.ELEMENT_TYPE_I:
case CorElementType.ELEMENT_TYPE_U:
#endif
case CorElementType.ELEMENT_TYPE_R8:
return Unsafe.As<byte, ulong>(ref pThisValue) == Unsafe.As<byte, ulong>(ref pOtherValue);
default:
Debug.Fail("Unknown enum underlying type");
return false;
}
}

public override int GetHashCode()
{
// CONTRACT with the runtime: GetHashCode of enum types is implemented as GetHashCode of the underlying type.
Expand Down Expand Up @@ -1214,7 +1308,8 @@ public int CompareTo(object? target)
case CorElementType.ELEMENT_TYPE_R8:
return Unsafe.As<byte, double>(ref pThisValue).CompareTo(Unsafe.As<byte, double>(ref pTargetValue));
default:
throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
Debug.Fail("Unknown enum underlying type");
return 0;
}
}
#endregion
Expand Down Expand Up @@ -1408,6 +1503,12 @@ private static RuntimeType ValidateRuntimeType(Type enumType)
throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
if (enumType is not RuntimeType rtType)
throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
#if CORERT
// Check for the unfortunate "typeof(Outer<>).InnerEnum" corner case.
// https://github.com/dotnet/runtime/issues/7976
if (enumType.ContainsGenericParameters)
throw new InvalidOperationException(SR.Format(SR.Arg_OpenType, enumType.ToString()));
#endif
return rtType;
}
}
Expand Down
3 changes: 0 additions & 3 deletions src/mono/System.Private.CoreLib/src/System/Enum.Mono.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ public partial class Enum
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern RuntimeType InternalGetUnderlyingType(RuntimeType enumType);

[MethodImpl(MethodImplOptions.InternalCall)]
private extern bool InternalHasFlag(Enum flags);

private static EnumInfo GetEnumInfo(RuntimeType enumType, bool getNames = true)
{
EnumInfo? entry = enumType.Cache.EnumInfo;
Expand Down
Loading

0 comments on commit 84150d9

Please sign in to comment.