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

Rewrite Enum.HasFlags and Enum.Equals in C# #59514

Merged
merged 2 commits into from
Sep 24, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
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 @@ -1199,7 +1191,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)
jkotas marked this conversation as resolved.
Show resolved Hide resolved
{
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