From 3362c4a12cf3a8c9e8427b5c5cb00ef5cdf0fcda Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 3 Apr 2024 20:56:15 +0000 Subject: [PATCH 01/15] Implement RuntimeHelpers.SizeOf --- .../CompilerServices/RuntimeHelpers.CoreCLR.cs | 12 ++++++++++++ .../CompilerServices/RuntimeHelpers.NativeAot.cs | 16 ++++++++++++++++ src/coreclr/vm/qcallentrypoints.cpp | 1 + src/coreclr/vm/reflectioninvocation.cpp | 15 +++++++++++++++ src/coreclr/vm/reflectioninvocation.h | 2 ++ .../System.Runtime/ref/System.Runtime.cs | 1 + .../CompilerServices/RuntimeHelpers.Mono.cs | 12 ++++++++++++ src/mono/mono/metadata/icall-def.h | 1 + src/mono/mono/metadata/icall.c | 7 +++++++ 9 files changed, 67 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 733e3a664bcc52..80ca9652b6f68e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -416,6 +416,18 @@ private static unsafe void DispatchTailCalls( } } } + + [LibraryImport(QCall, EntryPoint = "ReflectionInvocation_SizeOf")] + private static partial int SizeOf(QCallTypeHandle handle); + + public static int SizeOf(RuntimeTypeHandle type) + { + RuntimeTypeHandle typeLocal = type; + if (typeLocal.IsNullHandle()) + ThrowHelpers.ThrowArgumentNullException(ExceptionArgument.type); + + return SizeOf(new QCallTypeHandle(ref typeLocal)); + } } // Helper class to assist with unsafe pinning of arbitrary objects. // It's used by VM code. diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs index 262ddb9857b75f..24c0596314cdd0 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs @@ -361,6 +361,22 @@ public static unsafe object GetUninitializedObject( return RuntimeImports.RhNewObject(mt); } + + public static int SizeOf(RuntimeTypeHandle type) + { + RuntimeTypeHandle typeLocal = type; + if (typeLocal.IsNullHandle()) + ThrowHelpers.ThrowArgumentNullException(ExceptionArgument.type); + + MethodTable* mt = type.ToMethodTable(); + + if (mt->IsValueType) + { + return (int)mt->ValueTypeSize; + } + + return nint.Size; + } } // CLR arrays are laid out in memory as follows (multidimensional array bounds are optional): diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 1150c55aa36d1a..56841fe61f4042 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -337,6 +337,7 @@ static const Entry s_QCall[] = DllImportEntry(ReflectionInvocation_RunModuleConstructor) DllImportEntry(ReflectionInvocation_CompileMethod) DllImportEntry(ReflectionInvocation_PrepareMethod) + DllImportEntry(ReflectionInvocation_GetTypeSize) DllImportEntry(ReflectionSerialization_GetCreateUninitializedObjectInfo) #if defined(FEATURE_COMWRAPPERS) DllImportEntry(ComWrappers_GetIUnknownImpl) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index a7f88289d037a4..2cc88ffa3bc78e 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -2120,3 +2120,18 @@ FCIMPL2_IV(Object*, ReflectionEnum::InternalBoxEnum, ReflectClassBaseObject* tar return OBJECTREFToObject(ret); } FCIMPLEND + +extern "C" int32_t QCALLTYPE ReflectionInvocation_GetTypeSize(QCall::TypeHandle pType) +{ + QCALL_CONTRACT; + + int32_t size = 0; + + BEGIN_QCALL; + + size = pType.AsTypeHandle().GetSize(); + + END_QCALL; + + return size; +} diff --git a/src/coreclr/vm/reflectioninvocation.h b/src/coreclr/vm/reflectioninvocation.h index 967e9744790e93..aca638b33a0964 100644 --- a/src/coreclr/vm/reflectioninvocation.h +++ b/src/coreclr/vm/reflectioninvocation.h @@ -82,4 +82,6 @@ class ReflectionEnum { extern "C" void QCALLTYPE Enum_GetValuesAndNames(QCall::TypeHandle pEnumType, QCall::ObjectHandleOnStack pReturnValues, QCall::ObjectHandleOnStack pReturnNames, BOOL fGetNames); +extern "C" int32_t QCALLTYPE ReflectionInvocation_GetTypeSize(QCall::TypeHandle pType); + #endif // _REFLECTIONINVOCATION_H_ diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index ef4bc568fb56f5..4f55f4d87633c6 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -13175,6 +13175,7 @@ public static void ProbeForSufficientStack() { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Trimmer can't guarantee existence of class constructor")] public static void RunClassConstructor(System.RuntimeTypeHandle type) { } public static void RunModuleConstructor(System.ModuleHandle module) { } + public static int SizeOf(System.RuntimeTypeHandle type) { throw null; } public static bool TryEnsureSufficientExecutionStack() { throw null; } public delegate void CleanupCode(object? userData, bool exceptionThrown); public delegate void TryCode(object? userData); diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs index ca9ecdbe9f829c..da9446ac372611 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs @@ -211,5 +211,17 @@ private static extern unsafe IntPtr GetSpanDataFrom( [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern bool SufficientExecutionStack(); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern int SizeOf(QCallTypeHandle handle); + + public static int SizeOf(RuntimeTypeHandle type) + { + RuntimeTypeHandle typeLocal = type; + if (typeLocal.IsNullHandle()) + ThrowHelpers.ThrowArgumentNullException(ExceptionArgument.type); + + return SizeOf(new QCallTypeHandle(ref typeLocal)); + } } } diff --git a/src/mono/mono/metadata/icall-def.h b/src/mono/mono/metadata/icall-def.h index 84bb3df63a8cd7..b89deef5128afa 100644 --- a/src/mono/mono/metadata/icall-def.h +++ b/src/mono/mono/metadata/icall-def.h @@ -439,6 +439,7 @@ HANDLES(RUNH_7, "InternalGetHashCode", ves_icall_System_Runtime_CompilerServices HANDLES(RUNH_3a, "PrepareMethod", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_PrepareMethod, void, 3, (MonoMethod_ptr, gpointer, int)) HANDLES(RUNH_4, "RunClassConstructor", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_RunClassConstructor, void, 1, (MonoType_ptr)) HANDLES(RUNH_5, "RunModuleConstructor", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_RunModuleConstructor, void, 1, (MonoImage_ptr)) +HANDLES(RUNH_9, "SizeOf", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_SizeOf, gint32, 1, (MonoQCallTypeHandle)) NOHANDLES(ICALL(RUNH_5h, "SufficientExecutionStack", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_SufficientExecutionStack)) ICALL_TYPE(GCH, "System.Runtime.InteropServices.GCHandle", GCH_1) diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 6a3e33bc341af6..190d657afe3975 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -1210,6 +1210,13 @@ ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_PrepareMethod (MonoMeth // FIXME: Implement } +gint32 +ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_SizeOf (MonoQCallTypeHandle type, MonoError* error) +{ + int align; + return mono_type_size (type.type, &align); +} + MonoObjectHandle ves_icall_System_Object_MemberwiseClone (MonoObjectHandle this_obj, MonoError *error) { From 47c6a249a13156ce5ace00fba7113297bf75227d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 3 Apr 2024 22:58:10 +0000 Subject: [PATCH 02/15] Add tests and add various fixes. --- .../RuntimeHelpers.CoreCLR.cs | 11 +++++--- .../RuntimeHelpers.NativeAot.cs | 8 +++--- src/coreclr/vm/qcallentrypoints.cpp | 2 +- src/coreclr/vm/reflectioninvocation.cpp | 2 +- src/coreclr/vm/reflectioninvocation.h | 2 +- .../CompilerServices/RuntimeHelpersTests.cs | 26 +++++++++++++++++++ .../CompilerServices/RuntimeHelpers.Mono.cs | 11 +++++--- 7 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 80ca9652b6f68e..d5da572ebe1066 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -424,9 +424,14 @@ public static int SizeOf(RuntimeTypeHandle type) { RuntimeTypeHandle typeLocal = type; if (typeLocal.IsNullHandle()) - ThrowHelpers.ThrowArgumentNullException(ExceptionArgument.type); - - return SizeOf(new QCallTypeHandle(ref typeLocal)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); + + int result = SizeOf(new QCallTypeHandle(ref typeLocal)); + + if (result < 0) + throw new ArgumentException(SR.Arg_TypeNotSupported); + + return result; } } // Helper class to assist with unsafe pinning of arbitrary objects. diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs index 24c0596314cdd0..6e4b93d19c6b9a 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs @@ -362,12 +362,12 @@ public static unsafe object GetUninitializedObject( return RuntimeImports.RhNewObject(mt); } - public static int SizeOf(RuntimeTypeHandle type) + public static unsafe int SizeOf(RuntimeTypeHandle type) { RuntimeTypeHandle typeLocal = type; - if (typeLocal.IsNullHandle()) - ThrowHelpers.ThrowArgumentNullException(ExceptionArgument.type); - + if (typeLocal.IsNull) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); + MethodTable* mt = type.ToMethodTable(); if (mt->IsValueType) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 56841fe61f4042..233899582572e9 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -337,7 +337,7 @@ static const Entry s_QCall[] = DllImportEntry(ReflectionInvocation_RunModuleConstructor) DllImportEntry(ReflectionInvocation_CompileMethod) DllImportEntry(ReflectionInvocation_PrepareMethod) - DllImportEntry(ReflectionInvocation_GetTypeSize) + DllImportEntry(ReflectionInvocation_SizeOf) DllImportEntry(ReflectionSerialization_GetCreateUninitializedObjectInfo) #if defined(FEATURE_COMWRAPPERS) DllImportEntry(ComWrappers_GetIUnknownImpl) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index 2cc88ffa3bc78e..d567d7362585c2 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -2121,7 +2121,7 @@ FCIMPL2_IV(Object*, ReflectionEnum::InternalBoxEnum, ReflectClassBaseObject* tar } FCIMPLEND -extern "C" int32_t QCALLTYPE ReflectionInvocation_GetTypeSize(QCall::TypeHandle pType) +extern "C" int32_t QCALLTYPE ReflectionInvocation_SizeOf(QCall::TypeHandle pType) { QCALL_CONTRACT; diff --git a/src/coreclr/vm/reflectioninvocation.h b/src/coreclr/vm/reflectioninvocation.h index aca638b33a0964..d84e40f3a93ebd 100644 --- a/src/coreclr/vm/reflectioninvocation.h +++ b/src/coreclr/vm/reflectioninvocation.h @@ -82,6 +82,6 @@ class ReflectionEnum { extern "C" void QCALLTYPE Enum_GetValuesAndNames(QCall::TypeHandle pEnumType, QCall::ObjectHandleOnStack pReturnValues, QCall::ObjectHandleOnStack pReturnNames, BOOL fGetNames); -extern "C" int32_t QCALLTYPE ReflectionInvocation_GetTypeSize(QCall::TypeHandle pType); +extern "C" int32_t QCALLTYPE ReflectionInvocation_SizeOf(QCall::TypeHandle pType); #endif // _REFLECTIONINVOCATION_H_ diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index 5148ca0c488d80..4cda81dd3378fd 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -437,6 +437,32 @@ public static void FixedAddressValueTypeTest() Assert.Equal(fixedPtr1, fixedPtr2); } + + [InlineArray(4)] + private struct Byte4 + { + public byte b1; + } + + [Fact] + public static unsafe void SizeOf() + { + Assert.Equal(1, RuntimeHelpers.SizeOf(typeof(sbyte).TypeHandle)); + Assert.Equal(1, RuntimeHelpers.SizeOf(typeof(byte).TypeHandle)); + Assert.Equal(2, RuntimeHelpers.SizeOf(typeof(short).TypeHandle)); + Assert.Equal(2, RuntimeHelpers.SizeOf(typeof(ushort).TypeHandle)); + Assert.Equal(4, RuntimeHelpers.SizeOf(typeof(int).TypeHandle)); + Assert.Equal(4, RuntimeHelpers.SizeOf(typeof(uint).TypeHandle)); + Assert.Equal(8, RuntimeHelpers.SizeOf(typeof(long).TypeHandle)); + Assert.Equal(8, RuntimeHelpers.SizeOf(typeof(ulong).TypeHandle)); + Assert.Equal(4, RuntimeHelpers.SizeOf(typeof(float).TypeHandle)); + Assert.Equal(8, RuntimeHelpers.SizeOf(typeof(double).TypeHandle)); + Assert.Equal(4, RuntimeHelpers.SizeOf(typeof(Byte4).TypeHandle)); + Assert.Equal(nint.Size, RuntimeHelpers.SizeOf(typeof(void*).TypeHandle)); + Assert.Equal(nint.Size, RuntimeHelpers.SizeOf(typeof(delegate* ).TypeHandle)); + Assert.Throws(() => RuntimeHelpers.SizeOf(default)); + Assert.ThrowsAny(() => RuntimeHelpers.SizeOf(typeof(List<>).GetGenericArguments()[0].TypeHandle)); + } } public struct Age diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs index da9446ac372611..c9247236b20019 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs @@ -211,16 +211,19 @@ private static extern unsafe IntPtr GetSpanDataFrom( [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern bool SufficientExecutionStack(); - + [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern int SizeOf(QCallTypeHandle handle); public static int SizeOf(RuntimeTypeHandle type) { RuntimeTypeHandle typeLocal = type; - if (typeLocal.IsNullHandle()) - ThrowHelpers.ThrowArgumentNullException(ExceptionArgument.type); - + if (typeLocal.Value == IntPtr.Zero) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); + + if (Type.GetTypeFromHandle(type)!.IsGenericParameter) + throw new ArgumentException(SR.Arg_TypeNotSupported); + return SizeOf(new QCallTypeHandle(ref typeLocal)); } } From 16ea830b632bbc23168f2eafc1944dd968c4b5a1 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 3 Apr 2024 23:30:29 +0000 Subject: [PATCH 03/15] PR feedback --- .../Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 1 + src/coreclr/vm/reflectioninvocation.cpp | 2 +- .../System/Runtime/CompilerServices/RuntimeHelpersTests.cs | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index d5da572ebe1066..83b95ea9426c6b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -418,6 +418,7 @@ private static unsafe void DispatchTailCalls( } [LibraryImport(QCall, EntryPoint = "ReflectionInvocation_SizeOf")] + [SuppressGCTransition] private static partial int SizeOf(QCallTypeHandle handle); public static int SizeOf(RuntimeTypeHandle type) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index d567d7362585c2..7638cc30133a5a 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -2123,7 +2123,7 @@ FCIMPLEND extern "C" int32_t QCALLTYPE ReflectionInvocation_SizeOf(QCall::TypeHandle pType) { - QCALL_CONTRACT; + QCALL_CONTRACT_NO_GC_TRANSITION; int32_t size = 0; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index 4cda81dd3378fd..ab9eb6a27f4492 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -438,8 +438,8 @@ public static void FixedAddressValueTypeTest() Assert.Equal(fixedPtr1, fixedPtr2); } - [InlineArray(4)] - private struct Byte4 + [InlineArray(3)] + private struct Byte3 { public byte b1; } @@ -457,7 +457,7 @@ public static unsafe void SizeOf() Assert.Equal(8, RuntimeHelpers.SizeOf(typeof(ulong).TypeHandle)); Assert.Equal(4, RuntimeHelpers.SizeOf(typeof(float).TypeHandle)); Assert.Equal(8, RuntimeHelpers.SizeOf(typeof(double).TypeHandle)); - Assert.Equal(4, RuntimeHelpers.SizeOf(typeof(Byte4).TypeHandle)); + Assert.Equal(4, RuntimeHelpers.SizeOf(typeof(Byte3).TypeHandle)); Assert.Equal(nint.Size, RuntimeHelpers.SizeOf(typeof(void*).TypeHandle)); Assert.Equal(nint.Size, RuntimeHelpers.SizeOf(typeof(delegate* ).TypeHandle)); Assert.Throws(() => RuntimeHelpers.SizeOf(default)); From cb7ec92b153aa5578013a12bb2435e4bf56a73a7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 3 Apr 2024 17:02:46 -0700 Subject: [PATCH 04/15] Apply suggestions from code review Co-authored-by: Jan Kotas --- .../Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 5 ++--- .../Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs | 3 +-- src/coreclr/vm/reflectioninvocation.cpp | 4 ---- .../System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs | 5 ++--- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 83b95ea9426c6b..03261c2d2d5994 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -423,11 +423,10 @@ private static unsafe void DispatchTailCalls( public static int SizeOf(RuntimeTypeHandle type) { - RuntimeTypeHandle typeLocal = type; - if (typeLocal.IsNullHandle()) + if (type.IsNullHandle()) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); - int result = SizeOf(new QCallTypeHandle(ref typeLocal)); + int result = SizeOf(new QCallTypeHandle(ref type)); if (result < 0) throw new ArgumentException(SR.Arg_TypeNotSupported); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs index 6e4b93d19c6b9a..3f29630832ba75 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs @@ -364,8 +364,7 @@ public static unsafe object GetUninitializedObject( public static unsafe int SizeOf(RuntimeTypeHandle type) { - RuntimeTypeHandle typeLocal = type; - if (typeLocal.IsNull) + if (type.IsNull) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); MethodTable* mt = type.ToMethodTable(); diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index 7638cc30133a5a..67f16368e25198 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -2127,11 +2127,7 @@ extern "C" int32_t QCALLTYPE ReflectionInvocation_SizeOf(QCall::TypeHandle pType int32_t size = 0; - BEGIN_QCALL; - size = pType.AsTypeHandle().GetSize(); - END_QCALL; - return size; } diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs index c9247236b20019..2e43b4bcdfe7b3 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs @@ -217,14 +217,13 @@ private static extern unsafe IntPtr GetSpanDataFrom( public static int SizeOf(RuntimeTypeHandle type) { - RuntimeTypeHandle typeLocal = type; - if (typeLocal.Value == IntPtr.Zero) + if (type.Value == IntPtr.Zero) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); if (Type.GetTypeFromHandle(type)!.IsGenericParameter) throw new ArgumentException(SR.Arg_TypeNotSupported); - return SizeOf(new QCallTypeHandle(ref typeLocal)); + return SizeOf(new QCallTypeHandle(ref type)); } } } From 47d3b542861280596d6b95b5421e728f8e453263 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 3 Apr 2024 18:42:10 -0700 Subject: [PATCH 05/15] Update src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs Co-authored-by: Jan Kotas --- .../System/Runtime/CompilerServices/RuntimeHelpersTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index ab9eb6a27f4492..0fed5b11a043d8 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -457,7 +457,7 @@ public static unsafe void SizeOf() Assert.Equal(8, RuntimeHelpers.SizeOf(typeof(ulong).TypeHandle)); Assert.Equal(4, RuntimeHelpers.SizeOf(typeof(float).TypeHandle)); Assert.Equal(8, RuntimeHelpers.SizeOf(typeof(double).TypeHandle)); - Assert.Equal(4, RuntimeHelpers.SizeOf(typeof(Byte3).TypeHandle)); + Assert.Equal(3, RuntimeHelpers.SizeOf(typeof(Byte3).TypeHandle)); Assert.Equal(nint.Size, RuntimeHelpers.SizeOf(typeof(void*).TypeHandle)); Assert.Equal(nint.Size, RuntimeHelpers.SizeOf(typeof(delegate* ).TypeHandle)); Assert.Throws(() => RuntimeHelpers.SizeOf(default)); From c086418a4b3b2af2d4437a8fc1aa08d545f6bbcf Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 4 Apr 2024 12:25:10 -0700 Subject: [PATCH 06/15] Add more tests and throw for more invalid cases. --- .../Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 2 +- .../Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs | 7 +++++++ .../System/Runtime/CompilerServices/RuntimeHelpersTests.cs | 2 ++ .../System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs | 3 ++- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 03261c2d2d5994..03c41252dcfa5f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -428,7 +428,7 @@ public static int SizeOf(RuntimeTypeHandle type) int result = SizeOf(new QCallTypeHandle(ref type)); - if (result < 0) + if (result <= 0) throw new ArgumentException(SR.Arg_TypeNotSupported); return result; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs index 3f29630832ba75..7a4320faae2105 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs @@ -369,6 +369,13 @@ public static unsafe int SizeOf(RuntimeTypeHandle type) MethodTable* mt = type.ToMethodTable(); + if (mt->ElementType == Internal.Runtime.EETypeElementType.Void + || mt->IsGenericTypeDefinition + || mt->IsGenericParameter) + { + throw new ArgumentException(SR.Arg_TypeNotSupported); + } + if (mt->IsValueType) { return (int)mt->ValueTypeSize; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index 0fed5b11a043d8..c3290345cb048e 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -462,6 +462,8 @@ public static unsafe void SizeOf() Assert.Equal(nint.Size, RuntimeHelpers.SizeOf(typeof(delegate* ).TypeHandle)); Assert.Throws(() => RuntimeHelpers.SizeOf(default)); Assert.ThrowsAny(() => RuntimeHelpers.SizeOf(typeof(List<>).GetGenericArguments()[0].TypeHandle)); + Assert.ThrowsAny(() => RuntimeHelpers.SizeOf(typeof(List<>).TypeHandle)); + Assert.ThrowsAny(() => RuntimeHelpers.SizeOf(typeof(void).TypeHandle)); } } diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs index 2e43b4bcdfe7b3..9d2421d290718a 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs @@ -220,7 +220,8 @@ public static int SizeOf(RuntimeTypeHandle type) if (type.Value == IntPtr.Zero) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); - if (Type.GetTypeFromHandle(type)!.IsGenericParameter) + Type typeObj = Type.GetTypeFromHandle(type)!; + if (typeObj.IsGenericTypeDefinition || typeObj.IsGenericParameter || typeObj == typeof(void)) throw new ArgumentException(SR.Arg_TypeNotSupported); return SizeOf(new QCallTypeHandle(ref type)); From 84b4c0f6aecdc9f6a815f44644e2e312e2243533 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 4 Apr 2024 13:18:35 -0700 Subject: [PATCH 07/15] Generic parameter types don't have MethodTables in NativeAOT. --- .../Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs index 7a4320faae2105..9e2acee073ddaf 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs @@ -369,9 +369,8 @@ public static unsafe int SizeOf(RuntimeTypeHandle type) MethodTable* mt = type.ToMethodTable(); - if (mt->ElementType == Internal.Runtime.EETypeElementType.Void - || mt->IsGenericTypeDefinition - || mt->IsGenericParameter) + if (mt->ElementType == EETypeElementType.Void + || mt->IsGenericTypeDefinition) { throw new ArgumentException(SR.Arg_TypeNotSupported); } From 49e6521aafb6f3d75a82bb89b145165528a7cbd7 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 4 Apr 2024 14:34:09 -0700 Subject: [PATCH 08/15] Update src/coreclr/vm/reflectioninvocation.cpp --- src/coreclr/vm/reflectioninvocation.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index 67f16368e25198..d63bf54133cd66 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -2125,9 +2125,5 @@ extern "C" int32_t QCALLTYPE ReflectionInvocation_SizeOf(QCall::TypeHandle pType { QCALL_CONTRACT_NO_GC_TRANSITION; - int32_t size = 0; - - size = pType.AsTypeHandle().GetSize(); - - return size; + return pType.AsTypeHandle().GetSize(); } From f93baeeb78552dde87bbf88bb90adff3e866e144 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 4 Apr 2024 16:17:27 -0700 Subject: [PATCH 09/15] Fix CoreCLR --- .../Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 6 +++++- .../System.Private.CoreLib/src/System/RuntimeHandles.cs | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 03c41252dcfa5f..27cc39c6a0dd64 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -421,11 +421,15 @@ private static unsafe void DispatchTailCalls( [SuppressGCTransition] private static partial int SizeOf(QCallTypeHandle handle); - public static int SizeOf(RuntimeTypeHandle type) + public static unsafe int SizeOf(RuntimeTypeHandle type) { if (type.IsNullHandle()) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); + TypeHandle handle = type.GetNativeTypeHandle(); + if (!handle.IsTypeDesc && handle.AsMethodTable()->IsGenericTypeDefinition) + throw new ArgumentException(SR.Arg_TypeNotSupported); + int result = SizeOf(new QCallTypeHandle(ref type)); if (result <= 0) diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index c6c7b9a563b4da..001a9fcdfee6a8 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -87,6 +87,11 @@ internal bool IsNullHandle() return m_type == null; } + internal TypeHandle GetNativeTypeHandle() + { + return m_type.GetNativeTypeHandle(); + } + internal static bool IsTypeDefinition(RuntimeType type) { CorElementType corElemType = GetCorElementType(type); From 6898911eeceda8ecd8e655d3630eb2078993ef75 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 4 Apr 2024 16:25:48 -0700 Subject: [PATCH 10/15] Separate out case to skip on NativeAOT as it's not relavant on the platform --- .../Runtime/CompilerServices/RuntimeHelpersTests.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index c3290345cb048e..a10962ab652bff 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -461,10 +461,18 @@ public static unsafe void SizeOf() Assert.Equal(nint.Size, RuntimeHelpers.SizeOf(typeof(void*).TypeHandle)); Assert.Equal(nint.Size, RuntimeHelpers.SizeOf(typeof(delegate* ).TypeHandle)); Assert.Throws(() => RuntimeHelpers.SizeOf(default)); - Assert.ThrowsAny(() => RuntimeHelpers.SizeOf(typeof(List<>).GetGenericArguments()[0].TypeHandle)); Assert.ThrowsAny(() => RuntimeHelpers.SizeOf(typeof(List<>).TypeHandle)); Assert.ThrowsAny(() => RuntimeHelpers.SizeOf(typeof(void).TypeHandle)); } + + // We can't even get a RuntimeTypeHandle for a generic parameter type on NativeAOT, + // so we don't even get to the method we're testing. + // So, let's not even waste time running this test on NativeAOT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] + public static void SizeOfGenericParameter() + { + Assert.ThrowsAny(() => RuntimeHelpers.SizeOf(typeof(List<>).GetGenericArguments()[0].TypeHandle)); + } } public struct Age From 3423a52765aefc8abe5494673c1650e76399143c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 4 Apr 2024 16:56:15 -0700 Subject: [PATCH 11/15] Check ContainsGenericVariables --- .../Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 4 ---- .../Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs | 2 +- src/coreclr/vm/reflectioninvocation.cpp | 8 +++++++- .../Runtime/CompilerServices/RuntimeHelpersTests.cs | 2 ++ .../Runtime/CompilerServices/RuntimeHelpers.Mono.cs | 2 +- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 27cc39c6a0dd64..9f38a63e81bbe0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -426,10 +426,6 @@ public static unsafe int SizeOf(RuntimeTypeHandle type) if (type.IsNullHandle()) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); - TypeHandle handle = type.GetNativeTypeHandle(); - if (!handle.IsTypeDesc && handle.AsMethodTable()->IsGenericTypeDefinition) - throw new ArgumentException(SR.Arg_TypeNotSupported); - int result = SizeOf(new QCallTypeHandle(ref type)); if (result <= 0) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs index 9e2acee073ddaf..5bf64a8cc8db1a 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs @@ -370,7 +370,7 @@ public static unsafe int SizeOf(RuntimeTypeHandle type) MethodTable* mt = type.ToMethodTable(); if (mt->ElementType == EETypeElementType.Void - || mt->IsGenericTypeDefinition) + || mt->ContainsGenericParameters) { throw new ArgumentException(SR.Arg_TypeNotSupported); } diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index d63bf54133cd66..65b7453f64c74b 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -2125,5 +2125,11 @@ extern "C" int32_t QCALLTYPE ReflectionInvocation_SizeOf(QCall::TypeHandle pType { QCALL_CONTRACT_NO_GC_TRANSITION; - return pType.AsTypeHandle().GetSize(); + TypeHandle handle = pType.AsTypeHandle(); + + // -1 is the same sentinel value returned by GetSize for an invalid type. + if (handle.ContainsGenericVariables()) + return -1; + + return handle.GetSize(); } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index a10962ab652bff..0d93439cc9c72f 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -460,8 +460,10 @@ public static unsafe void SizeOf() Assert.Equal(3, RuntimeHelpers.SizeOf(typeof(Byte3).TypeHandle)); Assert.Equal(nint.Size, RuntimeHelpers.SizeOf(typeof(void*).TypeHandle)); Assert.Equal(nint.Size, RuntimeHelpers.SizeOf(typeof(delegate* ).TypeHandle)); + Assert.Equal(nint.Size, RuntimeHelpers.SizeOf(typeof(int).MakeByRefType().TypeHandle)); Assert.Throws(() => RuntimeHelpers.SizeOf(default)); Assert.ThrowsAny(() => RuntimeHelpers.SizeOf(typeof(List<>).TypeHandle)); + Assert.ThrowsAny(() => RuntimeHelpers.SizeOf(typeof(Dictionary<,>).MakeGenericType([ typeof(int), typeof(Dictionary<,>).GetGenericArguments()[1] ]).TypeHandle)); Assert.ThrowsAny(() => RuntimeHelpers.SizeOf(typeof(void).TypeHandle)); } diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs index 9d2421d290718a..63fbd7f6680f80 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs @@ -221,7 +221,7 @@ public static int SizeOf(RuntimeTypeHandle type) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); Type typeObj = Type.GetTypeFromHandle(type)!; - if (typeObj.IsGenericTypeDefinition || typeObj.IsGenericParameter || typeObj == typeof(void)) + if (typeObj.ContainsGenericParameters || typeObj.IsGenericParameter || typeObj == typeof(void)) throw new ArgumentException(SR.Arg_TypeNotSupported); return SizeOf(new QCallTypeHandle(ref type)); From 1c473f6c5d20c83e70d9798c67383b2444490b41 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 4 Apr 2024 17:59:23 -0700 Subject: [PATCH 12/15] Update RuntimeHelpers.NativeAot.cs Co-authored-by: Jan Kotas --- .../System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs index 5bf64a8cc8db1a..9e2acee073ddaf 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs @@ -370,7 +370,7 @@ public static unsafe int SizeOf(RuntimeTypeHandle type) MethodTable* mt = type.ToMethodTable(); if (mt->ElementType == EETypeElementType.Void - || mt->ContainsGenericParameters) + || mt->IsGenericTypeDefinition) { throw new ArgumentException(SR.Arg_TypeNotSupported); } From 21fde4e8a6ea80108ea84f445e4c086200574d63 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 5 Apr 2024 10:14:54 -0700 Subject: [PATCH 13/15] Add doc comments --- .../Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 9 +++++++++ .../Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs | 9 +++++++++ .../Runtime/CompilerServices/RuntimeHelpers.Mono.cs | 9 +++++++++ 3 files changed, 27 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 9f38a63e81bbe0..c6ed995eee38bd 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -421,6 +421,15 @@ private static unsafe void DispatchTailCalls( [SuppressGCTransition] private static partial int SizeOf(QCallTypeHandle handle); + /// + /// Get the size of an object of the given type. + /// + /// The type to get the size of. + /// The size of instances of the type. + /// The passed-in type is not a valid type to get the size of. + /// + /// This API has the same behavior as if you were to use the IL sizeof instruction with the passed in type as the operand. + /// public static unsafe int SizeOf(RuntimeTypeHandle type) { if (type.IsNullHandle()) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs index 5bf64a8cc8db1a..e6ca845ece3cb9 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs @@ -362,6 +362,15 @@ public static unsafe object GetUninitializedObject( return RuntimeImports.RhNewObject(mt); } + /// + /// Get the size of an object of the given type. + /// + /// The type to get the size of. + /// The size of instances of the type. + /// The passed-in type is not a valid type to get the size of. + /// + /// This API has the same behavior as if you were to use the IL sizeof instruction with the passed in type as the operand. + /// public static unsafe int SizeOf(RuntimeTypeHandle type) { if (type.IsNull) diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs index 63fbd7f6680f80..8ebc3bc5a4df64 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs @@ -215,6 +215,15 @@ private static extern unsafe IntPtr GetSpanDataFrom( [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern int SizeOf(QCallTypeHandle handle); + /// + /// Get the size of an object of the given type. + /// + /// The type to get the size of. + /// The size of instances of the type. + /// The passed-in type is not a valid type to get the size of. + /// + /// This API has the same behavior as if you were to use the IL sizeof instruction with the passed in type as the operand. + /// public static int SizeOf(RuntimeTypeHandle type) { if (type.Value == IntPtr.Zero) From 51a93b2c99f9652eb63cab143587fb01983162a6 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 5 Apr 2024 10:16:13 -0700 Subject: [PATCH 14/15] Separate out partially-specialized type test --- .../Runtime/CompilerServices/RuntimeHelpersTests.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index 0d93439cc9c72f..a4643beaba2b59 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -463,7 +463,6 @@ public static unsafe void SizeOf() Assert.Equal(nint.Size, RuntimeHelpers.SizeOf(typeof(int).MakeByRefType().TypeHandle)); Assert.Throws(() => RuntimeHelpers.SizeOf(default)); Assert.ThrowsAny(() => RuntimeHelpers.SizeOf(typeof(List<>).TypeHandle)); - Assert.ThrowsAny(() => RuntimeHelpers.SizeOf(typeof(Dictionary<,>).MakeGenericType([ typeof(int), typeof(Dictionary<,>).GetGenericArguments()[1] ]).TypeHandle)); Assert.ThrowsAny(() => RuntimeHelpers.SizeOf(typeof(void).TypeHandle)); } @@ -475,6 +474,15 @@ public static void SizeOfGenericParameter() { Assert.ThrowsAny(() => RuntimeHelpers.SizeOf(typeof(List<>).GetGenericArguments()[0].TypeHandle)); } + + // We can't even get a RuntimeTypeHandle for a partially-open-generic type on NativeAOT, + // so we don't even get to the method we're testing. + // So, let's not even waste time running this test on NativeAOT + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] + public static void SizeOfPartiallyOpenGeneric() + { + Assert.ThrowsAny(() => RuntimeHelpers.SizeOf(typeof(Dictionary<,>).MakeGenericType(typeof(object), typeof(Dictionary<,>).GetGenericArguments()[1]).TypeHandle)); + } } public struct Age From ed05d1a07334e3192f27f9cfed7b0f72dea3516d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 5 Apr 2024 15:16:23 -0700 Subject: [PATCH 15/15] Update doc comment to refer to `Unsafe.SizeOf` --- .../System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 2 +- .../System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs | 2 +- .../src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index c6ed995eee38bd..f5c42b74515a55 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -428,7 +428,7 @@ private static unsafe void DispatchTailCalls( /// The size of instances of the type. /// The passed-in type is not a valid type to get the size of. /// - /// This API has the same behavior as if you were to use the IL sizeof instruction with the passed in type as the operand. + /// This API returns the same value as for the type that represents. /// public static unsafe int SizeOf(RuntimeTypeHandle type) { diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs index 0f01d01addcbc1..d221bc00a20611 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs @@ -369,7 +369,7 @@ public static unsafe object GetUninitializedObject( /// The size of instances of the type. /// The passed-in type is not a valid type to get the size of. /// - /// This API has the same behavior as if you were to use the IL sizeof instruction with the passed in type as the operand. + /// This API returns the same value as for the type that represents. /// public static unsafe int SizeOf(RuntimeTypeHandle type) { diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs index 8ebc3bc5a4df64..e16e5ec178d38e 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs @@ -222,7 +222,7 @@ private static extern unsafe IntPtr GetSpanDataFrom( /// The size of instances of the type. /// The passed-in type is not a valid type to get the size of. /// - /// This API has the same behavior as if you were to use the IL sizeof instruction with the passed in type as the operand. + /// This API returns the same value as for the type that represents. /// public static int SizeOf(RuntimeTypeHandle type) {