Skip to content

Commit b9cd89a

Browse files
authored
Reimplement a few helpers with new RuntimeHelpers APIs (#100846)
1 parent 53608c5 commit b9cd89a

File tree

14 files changed

+59
-233
lines changed

14 files changed

+59
-233
lines changed

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ public abstract partial class Enum
1414
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Enum_GetValuesAndNames")]
1515
private static partial void GetEnumValuesAndNames(QCallTypeHandle enumType, ObjectHandleOnStack values, ObjectHandleOnStack names, Interop.BOOL getNames);
1616

17-
[MethodImpl(MethodImplOptions.InternalCall)]
18-
private static extern object InternalBoxEnum(RuntimeType enumType, long value);
19-
2017
[MethodImpl(MethodImplOptions.InternalCall)]
2118
private static extern unsafe CorElementType InternalGetCorElementType(MethodTable* pMT);
2219

src/coreclr/System.Private.CoreLib/src/System/Reflection/MdConstant.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ internal static class MdConstant
7272
#endregion
7373
}
7474

75-
return RuntimeType.CreateEnum(fieldType, defaultValue);
75+
return Enum.ToObject(fieldType, defaultValue);
7676
}
7777
else if (fieldType == typeof(DateTime))
7878
{

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

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3664,9 +3664,6 @@ public override Type MakeArrayType(int rank)
36643664
[MethodImpl(MethodImplOptions.InternalCall)]
36653665
private static extern bool CanValueSpecialCast(RuntimeType valueType, RuntimeType targetType);
36663666

3667-
[MethodImpl(MethodImplOptions.InternalCall)]
3668-
internal static extern object AllocateValueType(RuntimeType type, object? value);
3669-
36703667
private CheckValueStatus TryChangeTypeSpecial(ref object value)
36713668
{
36723669
Pointer? pointer = value as Pointer;
@@ -3973,14 +3970,6 @@ internal object GetUninitializedObject()
39733970

39743971
#region Legacy internal static
39753972

3976-
[MethodImpl(MethodImplOptions.InternalCall)]
3977-
private static extern object _CreateEnum(RuntimeType enumType, long value);
3978-
3979-
internal static object CreateEnum(RuntimeType enumType, long value)
3980-
{
3981-
return _CreateEnum(enumType, value);
3982-
}
3983-
39843973
#if FEATURE_COMINTEROP
39853974
[MethodImpl(MethodImplOptions.InternalCall)]
39863975
private extern object InvokeDispMethod(

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

Lines changed: 2 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,8 @@ internal static EnumInfo<TStorage> GetEnumInfo<TStorage>(RuntimeType enumType, b
6262
}
6363
#pragma warning restore
6464

65-
private static unsafe object InternalBoxEnum(Type enumType, long value)
66-
{
67-
return ToObject(enumType.TypeHandle.ToMethodTable(), value);
68-
}
65+
internal static unsafe object ToObject(MethodTable* mt, long value)
66+
=> InternalBoxEnum(new RuntimeTypeHandle(mt), value);
6967

7068
private static unsafe CorElementType InternalGetCorElementType(RuntimeType rt)
7169
{
@@ -151,51 +149,5 @@ internal static Type InternalGetUnderlyingType(RuntimeType enumType)
151149

152150
return GetEnumInfo(enumType).UnderlyingType;
153151
}
154-
155-
[Conditional("BIGENDIAN")]
156-
private static unsafe void AdjustForEndianness(ref byte* pValue, MethodTable* enumEEType)
157-
{
158-
// On Debug builds, include the big-endian code to help deter bitrot (the "Conditional("BIGENDIAN")" will prevent it from executing on little-endian).
159-
// On Release builds, exclude code to deter IL bloat and toolchain work.
160-
#if BIGENDIAN || DEBUG
161-
EETypeElementType elementType = enumEEType->ElementType;
162-
switch (elementType)
163-
{
164-
case EETypeElementType.SByte:
165-
case EETypeElementType.Byte:
166-
pValue += sizeof(long) - sizeof(byte);
167-
break;
168-
169-
case EETypeElementType.Int16:
170-
case EETypeElementType.UInt16:
171-
pValue += sizeof(long) - sizeof(short);
172-
break;
173-
174-
case EETypeElementType.Int32:
175-
case EETypeElementType.UInt32:
176-
pValue += sizeof(long) - sizeof(int);
177-
break;
178-
179-
case EETypeElementType.Int64:
180-
case EETypeElementType.UInt64:
181-
break;
182-
183-
default:
184-
throw new NotSupportedException();
185-
}
186-
#endif //BIGENDIAN || DEBUG
187-
}
188-
189-
#region ToObject
190-
191-
internal static unsafe object ToObject(MethodTable* enumEEType, long value)
192-
{
193-
Debug.Assert(enumEEType->IsEnum);
194-
195-
byte* pValue = (byte*)&value;
196-
AdjustForEndianness(ref pValue, enumEEType);
197-
return RuntimeImports.RhBox(enumEEType, ref *pValue);
198-
}
199-
#endregion
200152
}
201153
}

src/coreclr/vm/ecalllist.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ FCFuncEnd()
5656

5757
FCFuncStart(gEnumFuncs)
5858
FCFuncElement("InternalGetCorElementType", ReflectionEnum::InternalGetCorElementType)
59-
FCFuncElement("InternalBoxEnum", ReflectionEnum::InternalBoxEnum)
6059
FCFuncEnd()
6160

6261
FCFuncStart(gObjectFuncs)
@@ -114,9 +113,7 @@ FCFuncEnd()
114113

115114
FCFuncStart(gSystem_RuntimeType)
116115
FCFuncElement("GetGUID", ReflectionInvocation::GetGUID)
117-
FCFuncElement("_CreateEnum", ReflectionInvocation::CreateEnum)
118116
FCFuncElement("CanValueSpecialCast", ReflectionInvocation::CanValueSpecialCast)
119-
FCFuncElement("AllocateValueType", ReflectionInvocation::AllocateValueType)
120117
#if defined(FEATURE_COMINTEROP)
121118
FCFuncElement("InvokeDispMethod", ReflectionInvocation::InvokeDispMethod)
122119
#endif // defined(FEATURE_COMINTEROP)

src/coreclr/vm/reflectioninvocation.cpp

Lines changed: 1 addition & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -113,54 +113,6 @@ FCIMPL2(FC_BOOL_RET, ReflectionInvocation::CanValueSpecialCast, ReflectClassBase
113113
}
114114
FCIMPLEND
115115

116-
/// <summary>
117-
/// Allocate the value type and copy the optional value into it.
118-
/// </summary>
119-
FCIMPL2(Object*, ReflectionInvocation::AllocateValueType, ReflectClassBaseObject *pTargetTypeUNSAFE, Object *valueUNSAFE) {
120-
CONTRACTL
121-
{
122-
FCALL_CHECK;
123-
PRECONDITION(CheckPointer(pTargetTypeUNSAFE));
124-
PRECONDITION(CheckPointer(valueUNSAFE, NULL_OK));
125-
}
126-
CONTRACTL_END;
127-
128-
struct _gc
129-
{
130-
REFLECTCLASSBASEREF refTargetType;
131-
OBJECTREF value;
132-
OBJECTREF obj;
133-
}gc;
134-
135-
gc.value = ObjectToOBJECTREF(valueUNSAFE);
136-
gc.obj = gc.value;
137-
gc.refTargetType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTargetTypeUNSAFE);
138-
139-
HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
140-
141-
TypeHandle targetType = gc.refTargetType->GetType();
142-
143-
// This method is only intended for value types; it is not called directly by any public APIs
144-
// so we don't expect validation issues here.
145-
_ASSERTE(targetType.IsValueType());
146-
147-
MethodTable* allocMT = targetType.AsMethodTable();
148-
_ASSERTE(!allocMT->IsByRefLike());
149-
150-
gc.obj = allocMT->Allocate();
151-
_ASSERTE(gc.obj != NULL);
152-
153-
if (gc.value != NULL) {
154-
_ASSERTE(allocMT->IsEquivalentTo(gc.value->GetMethodTable()));
155-
CopyValueClass(gc.obj->UnBox(), gc.value->UnBox(), allocMT);
156-
}
157-
158-
HELPER_METHOD_FRAME_END();
159-
160-
return OBJECTREFToObject(gc.obj);
161-
}
162-
FCIMPLEND
163-
164116
FCIMPL6(void, RuntimeFieldHandle::SetValue, ReflectFieldObject *pFieldUNSAFE, Object *targetUNSAFE, Object *valueUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE, ReflectClassBaseObject *pDeclaringTypeUNSAFE, CLR_BOOL *pIsClassInitialized) {
165117
CONTRACTL
166118
{
@@ -1535,24 +1487,6 @@ FCIMPL4(void, ReflectionInvocation::MakeTypedReference, TypedByRef * value, Obje
15351487
}
15361488
FCIMPLEND
15371489

1538-
FCIMPL2_IV(Object*, ReflectionInvocation::CreateEnum, ReflectClassBaseObject *pTypeUNSAFE, INT64 value) {
1539-
FCALL_CONTRACT;
1540-
1541-
REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE);
1542-
1543-
TypeHandle typeHandle = refType->GetType();
1544-
_ASSERTE(typeHandle.IsEnum());
1545-
OBJECTREF obj = NULL;
1546-
HELPER_METHOD_FRAME_BEGIN_RET_1(refType);
1547-
MethodTable *pEnumMT = typeHandle.AsMethodTable();
1548-
obj = pEnumMT->Box(ArgSlotEndiannessFixup ((ARG_SLOT*)&value,
1549-
pEnumMT->GetNumInstanceFieldBytes()));
1550-
1551-
HELPER_METHOD_FRAME_END();
1552-
return OBJECTREFToObject(obj);
1553-
}
1554-
FCIMPLEND
1555-
15561490
#ifdef FEATURE_COMINTEROP
15571491
FCIMPL8(Object*, ReflectionInvocation::InvokeDispMethod, ReflectClassBaseObject* refThisUNSAFE,
15581492
StringObject* nameUNSAFE,
@@ -1965,7 +1899,7 @@ extern "C" void QCALLTYPE ReflectionSerialization_GetCreateUninitializedObjectIn
19651899
bool fHasSideEffectsUnused;
19661900
*ppfnAllocator = CEEJitInfo::getHelperFtnStatic(CEEInfo::getNewHelperStatic(pMT, &fHasSideEffectsUnused));
19671901
*pvAllocatorFirstArg = pMT;
1968-
1902+
19691903
pMT->EnsureInstanceActive();
19701904

19711905
if (pMT->HasPreciseInitCctors())
@@ -2105,22 +2039,6 @@ extern "C" void QCALLTYPE Enum_GetValuesAndNames(QCall::TypeHandle pEnumType, QC
21052039
END_QCALL;
21062040
}
21072041

2108-
FCIMPL2_IV(Object*, ReflectionEnum::InternalBoxEnum, ReflectClassBaseObject* target, INT64 value) {
2109-
FCALL_CONTRACT;
2110-
2111-
VALIDATEOBJECT(target);
2112-
OBJECTREF ret = NULL;
2113-
2114-
MethodTable* pMT = target->GetType().AsMethodTable();
2115-
HELPER_METHOD_FRAME_BEGIN_RET_0();
2116-
2117-
ret = pMT->Box(ArgSlotEndiannessFixup((ARG_SLOT*)&value, pMT->GetNumInstanceFieldBytes()));
2118-
2119-
HELPER_METHOD_FRAME_END();
2120-
return OBJECTREFToObject(ret);
2121-
}
2122-
FCIMPLEND
2123-
21242042
extern "C" int32_t QCALLTYPE ReflectionInvocation_SizeOf(QCall::TypeHandle pType)
21252043
{
21262044
QCALL_CONTRACT_NO_GC_TRANSITION;

src/coreclr/vm/reflectioninvocation.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,9 @@ class ReflectionInvocation {
5757
static FCDECL8(Object*, InvokeDispMethod, ReflectClassBaseObject* refThisUNSAFE, StringObject* nameUNSAFE, INT32 invokeAttr, Object* targetUNSAFE, PTRArray* argsUNSAFE, PTRArray* byrefModifiersUNSAFE, LCID lcid, PTRArray* namedParametersUNSAFE);
5858
#endif // FEATURE_COMINTEROP
5959
static FCDECL2(void, GetGUID, ReflectClassBaseObject* refThisUNSAFE, GUID * result);
60-
static FCDECL2_IV(Object*, CreateEnum, ReflectClassBaseObject *pTypeUNSAFE, INT64 value);
6160

6261
// helper fcalls for invocation
6362
static FCDECL2(FC_BOOL_RET, CanValueSpecialCast, ReflectClassBaseObject *valueType, ReflectClassBaseObject *targetType);
64-
static FCDECL2(Object*, AllocateValueType, ReflectClassBaseObject *targetType, Object *valueUNSAFE);
6563
};
6664

6765
extern "C" void QCALLTYPE ReflectionInvocation_CompileMethod(MethodDesc * pMD);
@@ -77,7 +75,6 @@ extern "C" void QCALLTYPE ReflectionSerialization_GetCreateUninitializedObjectIn
7775
class ReflectionEnum {
7876
public:
7977
static FCDECL1(INT32, InternalGetCorElementType, MethodTable* pMT);
80-
static FCDECL2_IV(Object*, InternalBoxEnum, ReflectClassBaseObject* pEnumType, INT64 value);
8178
};
8279

8380
extern "C" void QCALLTYPE Enum_GetValuesAndNames(QCall::TypeHandle pEnumType, QCall::ObjectHandleOnStack pReturnValues, QCall::ObjectHandleOnStack pReturnNames, BOOL fGetNames);

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

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using System.Numerics;
1313
using System.Reflection;
1414
using System.Runtime.CompilerServices;
15+
using System.Runtime.InteropServices;
1516

1617
#pragma warning disable 8500 // Allow taking address of managed types
1718

@@ -768,7 +769,7 @@ private static unsafe bool TryParse(Type enumType, ReadOnlySpan<char> value, boo
768769
break;
769770
}
770771

771-
result = parsed ? InternalBoxEnum(rt, longScratch) : null;
772+
result = parsed ? InternalBoxEnum(rt.TypeHandle, longScratch) : null;
772773
return parsed;
773774

774775
[MethodImpl(MethodImplOptions.NoInlining)]
@@ -2225,31 +2226,47 @@ public static object ToObject(Type enumType, object value)
22252226

22262227
[CLSCompliant(false)]
22272228
public static object ToObject(Type enumType, sbyte value) =>
2228-
InternalBoxEnum(ValidateRuntimeType(enumType), value);
2229+
InternalBoxEnum(ValidateRuntimeType(enumType).TypeHandle, value);
22292230

22302231
public static object ToObject(Type enumType, short value) =>
2231-
InternalBoxEnum(ValidateRuntimeType(enumType), value);
2232+
InternalBoxEnum(ValidateRuntimeType(enumType).TypeHandle, value);
22322233

22332234
public static object ToObject(Type enumType, int value) =>
2234-
InternalBoxEnum(ValidateRuntimeType(enumType), value);
2235+
InternalBoxEnum(ValidateRuntimeType(enumType).TypeHandle, value);
22352236

22362237
public static object ToObject(Type enumType, byte value) =>
2237-
InternalBoxEnum(ValidateRuntimeType(enumType), value);
2238+
InternalBoxEnum(ValidateRuntimeType(enumType).TypeHandle, value);
22382239

22392240
[CLSCompliant(false)]
22402241
public static object ToObject(Type enumType, ushort value) =>
2241-
InternalBoxEnum(ValidateRuntimeType(enumType), value);
2242+
InternalBoxEnum(ValidateRuntimeType(enumType).TypeHandle, value);
22422243

22432244
[CLSCompliant(false)]
22442245
public static object ToObject(Type enumType, uint value) =>
2245-
InternalBoxEnum(ValidateRuntimeType(enumType), value);
2246+
InternalBoxEnum(ValidateRuntimeType(enumType).TypeHandle, value);
22462247

22472248
public static object ToObject(Type enumType, long value) =>
2248-
InternalBoxEnum(ValidateRuntimeType(enumType), value);
2249+
InternalBoxEnum(ValidateRuntimeType(enumType).TypeHandle, value);
22492250

22502251
[CLSCompliant(false)]
22512252
public static object ToObject(Type enumType, ulong value) =>
2252-
InternalBoxEnum(ValidateRuntimeType(enumType), unchecked((long)value));
2253+
InternalBoxEnum(ValidateRuntimeType(enumType).TypeHandle, unchecked((long)value));
2254+
2255+
private static object InternalBoxEnum(RuntimeTypeHandle type, long value)
2256+
{
2257+
ReadOnlySpan<byte> rawData = MemoryMarshal.AsBytes(new ReadOnlySpan<long>(ref value));
2258+
// On little-endian systems, we can always use the pointer to the start of the scratch space
2259+
// as memory layout since the least-significant bit is at the lowest address.
2260+
// For big-endian systems, the least-significant bit is at the highest address, so we need to adjust
2261+
// our starting ref to the correct offset from the end of the scratch space to get the value to box.
2262+
if (!BitConverter.IsLittleEndian)
2263+
{
2264+
int size = RuntimeHelpers.SizeOf(type);
2265+
rawData = rawData.Slice(sizeof(long) - size);
2266+
}
2267+
2268+
return RuntimeHelpers.Box(ref MemoryMarshal.GetReference(rawData), type)!;
2269+
}
22532270

22542271
internal static bool AreSequentialFromZero<TStorage>(TStorage[] values) where TStorage : struct, INumber<TStorage>
22552272
{

src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ private static bool TryByRefFastPath(RuntimeType type, ref object arg)
402402
{
403403
if (sigElementType.IsValueType)
404404
{
405+
Debug.Assert(!sigElementType.IsNullableOfT, "A true boxed Nullable<T> should never be here.");
405406
// Make a copy to prevent the boxed instance from being directly modified by the method.
406407
arg = RuntimeType.AllocateValueType(sigElementType, arg);
407408
}

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,31 @@ internal bool CheckValue(
881881
return false;
882882
}
883883

884+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:UnrecognizedReflectionPattern",
885+
Justification = "AllocateValueType is only called on a ValueType. You can always create an instance of a ValueType.")]
886+
[return: NotNullIfNotNull(nameof(value))]
887+
internal static object? AllocateValueType(RuntimeType type, object? value)
888+
{
889+
Debug.Assert(type.IsValueType);
890+
Debug.Assert(!type.IsByRefLike);
891+
892+
if (value is not null)
893+
{
894+
// Make a copy of the provided value by re-boxing the existing value's underlying data.
895+
Debug.Assert(type.IsEquivalentTo(value.GetType()));
896+
return RuntimeHelpers.Box(ref RuntimeHelpers.GetRawData(value), type.TypeHandle)!;
897+
}
898+
899+
if (type.IsNullableOfT)
900+
{
901+
// If the type is Nullable<T>, then create a true boxed Nullable<T> of the default Nullable<T> value.
902+
return RuntimeMethodHandle.ReboxToNullable(null, type);
903+
}
904+
905+
// Otherwise, just create a default instance of the type.
906+
return RuntimeHelpers.GetUninitializedObject(type);
907+
}
908+
884909
private CheckValueStatus TryChangeType(ref object? value, ref bool copyBack)
885910
{
886911
RuntimeType? sigElementType;

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,12 @@ public partial class Enum
1515
[MethodImpl(MethodImplOptions.InternalCall)]
1616
private static extern void GetEnumValuesAndNames(QCallTypeHandle enumType, out ulong[] values, out string[] names);
1717

18-
[MethodImpl(MethodImplOptions.InternalCall)]
19-
private static extern void InternalBoxEnum(QCallTypeHandle enumType, ObjectHandleOnStack res, long value);
20-
2118
[MethodImpl(MethodImplOptions.InternalCall)]
2219
private static extern CorElementType InternalGetCorElementType(QCallTypeHandle enumType);
2320

2421
[MethodImpl(MethodImplOptions.InternalCall)]
2522
private static extern void InternalGetUnderlyingType(QCallTypeHandle enumType, ObjectHandleOnStack res);
2623

27-
private static object InternalBoxEnum(RuntimeType enumType, long value)
28-
{
29-
object? res = null;
30-
InternalBoxEnum(new QCallTypeHandle(ref enumType), ObjectHandleOnStack.Create(ref res), value);
31-
return res!;
32-
}
33-
3424
private static unsafe CorElementType InternalGetCorElementType(RuntimeType rt)
3525
{
3626
Debug.Assert(rt.IsActualEnum);

0 commit comments

Comments
 (0)