From 06776cd43481e3096e6efe3dbf0063bcbf8fd50a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 2 Apr 2024 23:11:01 +0000 Subject: [PATCH 01/12] Implement RuntimeHelpers.Box --- .../RuntimeHelpers.CoreCLR.cs | 25 +++++ .../RuntimeHelpers.NativeAot.cs | 20 ++++ .../src/System/TypedReference.cs | 20 +--- .../src/Resources/Strings.resx | 3 + .../System.Runtime/ref/System.Runtime.cs | 1 + .../CompilerServices/RuntimeHelpersTests.cs | 104 ++++++++++++++++++ .../CompilerServices/RuntimeHelpers.Mono.cs | 33 ++++++ src/mono/mono/metadata/icall-def.h | 1 + src/mono/mono/metadata/icall-table.h | 2 + src/mono/mono/metadata/icall.c | 20 ++++ 10 files changed, 215 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 733e3a664bcc52..0e716f1d70b1ae 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,31 @@ private static unsafe void DispatchTailCalls( } } } + + public static unsafe object? Box(ref byte target, RuntimeTypeHandle type) + { + if (type.IsNullHandle()) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); + + RuntimeType rtType = (RuntimeType)Type.GetTypeFromHandle(type)!; + + if (rtType.IsByRefLike) + throw new NotSupportedException(SR.NotSupported_ByRefLike); + + if (rtType.IsPointer || rtType.IsFunctionPointer) + throw new NotSupportedException(SR.NotSupported_BoxedPointer); + + if (rtType.IsActualValueType) + { + object? result = Box(rtType.GetNativeTypeHandle().AsMethodTable(), ref target); + GC.KeepAlive(rtType); + return result; + } + else + { + return Unsafe.As(ref target); + } + } } // 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..bf5405ababa4db 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,26 @@ public static unsafe object GetUninitializedObject( return RuntimeImports.RhNewObject(mt); } + + public static unsafe object Box(ref byte target, RuntimeTypeHandle type) + { + if (type.IsNull) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); + + // Compatibility with CoreCLR, throw on a null reference to the unboxed data. + if (Unsafe.IsNullRef(ref target)) + throw new NullReferenceException(); + + MethodTable* eeType = type.ToMethodTable(); + + if (eeType->IsByRefLike) + throw new NotSupportedException(SR.NotSupported_ByRefLike); + + if (eeType->IsPointer || eeType->IsFunctionPointer) + throw new NotSupportedException(SR.NotSupported_BoxedPointer); + + return RuntimeImports.RhBoxAny(ref target, eeType); + } } // CLR arrays are laid out in memory as follows (multidimensional array bounds are optional): diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypedReference.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypedReference.cs index 3b209cd489c44a..0ab8ffa1ceee3e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypedReference.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypedReference.cs @@ -50,23 +50,15 @@ internal static RuntimeTypeHandle RawTargetTypeToken(TypedReference value) public static unsafe object ToObject(TypedReference value) { - RuntimeTypeHandle typeHandle = value._typeHandle; - if (typeHandle.IsNull) - ThrowHelper.ThrowArgumentException_ArgumentNull_TypedRefType(); + RuntimeTypeHandle handle = RawTargetTypeToken(value); - MethodTable* eeType = typeHandle.ToMethodTable(); - if (eeType->IsValueType) + MethodTable* mt = handle.ToMethodTable(); + if (mt->IsPointer || mt->IsFunctionPointer) { - return RuntimeImports.RhBox(eeType, ref value.Value); - } - else if (eeType->IsPointer || eeType->IsFunctionPointer) - { - return RuntimeImports.RhBox(MethodTable.Of(), ref value.Value); - } - else - { - return Unsafe.As(ref value.Value); + handle = typeof(UIntPtr).TypeHandle; } + + return RuntimeHelpers.Box(ref value.Value, handle); } public static void SetTypedReference(TypedReference target, object? value) { throw new NotSupportedException(); } diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index bbc4b27c51ddeb..4a6711689f416b 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -2954,6 +2954,9 @@ Assembly.LoadFrom with hashValue is not supported. + + Cannot create boxed pointer values. + Cannot create boxed ByRef-like values. diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index ef4bc568fb56f5..0371e3037e2653 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -13149,6 +13149,7 @@ public static partial class RuntimeHelpers [System.ObsoleteAttribute("OffsetToStringData has been deprecated. Use string.GetPinnableReference() instead.")] public static int OffsetToStringData { get { throw null; } } public static System.IntPtr AllocateTypeAssociatedMemory(System.Type type, int size) { throw null; } + public static object? Box(ref byte target, System.RuntimeTypeHandle type) { throw null; } public static System.ReadOnlySpan CreateSpan(System.RuntimeFieldHandle fldHandle) { throw null; } public static void EnsureSufficientExecutionStack() { } public static new bool Equals(object? o1, object? o2) { throw null; } 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..e93acdd87abae5 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,110 @@ public static void FixedAddressValueTypeTest() Assert.Equal(fixedPtr1, fixedPtr2); } + + [Fact] + public static void BoxPrimitive() + { + int value = 4; + object result = RuntimeHelpers.Box(ref Unsafe.As(ref value), typeof(int).TypeHandle); + Assert.Equal(value, Assert.IsType(result)); + } + + [Fact] + public static void BoxPointer() + { + Assert.Throws(() => + { + nint value = 3; + object result = RuntimeHelpers.Box(ref Unsafe.As(ref value), typeof(void*).TypeHandle); + }); + } + + [StructLayout(LayoutKind.Sequential)] + private ref struct ByRefLikeType + { + public int i; + } + + [Fact] + public static void BoxByRefLike() + { + Assert.Throws(() => + { + int value = 3; + object result = RuntimeHelpers.Box(ref Unsafe.As(ref value), typeof(ByRefLikeType).TypeHandle); + }); + } + + [Fact] + public static void BoxStruct() + { + Span buffer = [0, 42, int.MaxValue]; + StructWithoutReferences expected = new() + { + a = buffer[0], + b = buffer[1], + c = buffer[2] + }; + object result = RuntimeHelpers.Box(ref MemoryMarshal.AsBytes(buffer)[0], typeof(StructWithoutReferences).TypeHandle); + + Assert.Equal(expected, Assert.IsType(result)); + } + + [StructLayout(LayoutKind.Sequential)] + private struct GenericStruct + { + public T data; + } + + [Fact] + public static void BoxUnmanagedGenericStruct() + { + int value = 3; + object result = RuntimeHelpers.Box(ref Unsafe.As(ref value), typeof(GenericStruct).TypeHandle); + + Assert.Equal(value, Assert.IsType>(result).data); + } + + [Fact] + public static void BoxManagedGenericStruct() + { + object value = new(); + object result = RuntimeHelpers.Box(ref Unsafe.As(ref value), typeof(GenericStruct).TypeHandle); + + Assert.Same(value, Assert.IsType>(result).data); + } + + [Fact] + public static void BoxNullable() + { + float? value = 3.14f; + object result = RuntimeHelpers.Box(ref Unsafe.As(ref value), typeof(float?).TypeHandle); + Assert.Equal(value, Assert.IsType(result)); + } + + [Fact] + public static void NullBox() + { + Assert.Throws(() => RuntimeHelpers.Box(ref Unsafe.NullRef(), typeof(byte).TypeHandle)); + } + + [Fact] + public static void BoxInvalidType() + { + Assert.Throws(() => + { + byte value = 3; + RuntimeHelpers.Box(ref value, default(RuntimeTypeHandle)); + }); + } + + [Fact] + public static void BoxReferenceType() + { + string str = "ABC"; + Assert.Same(str, RuntimeHelpers.Box(ref Unsafe.As(ref str), typeof(string).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 ca9ecdbe9f829c..f313891e955676 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,38 @@ private static extern unsafe IntPtr GetSpanDataFrom( [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern bool SufficientExecutionStack(); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void InternalBox(QCallTypeHandle type, ref byte target, ObjectHandleOnStack result); + + public static object? Box(ref byte target, RuntimeTypeHandle type) + { + if (type.Value is 0) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); + + // Compatibility with CoreCLR, throw on a null reference to the unboxed data. + if (Unsafe.IsNullRef(ref target)) + throw new NullReferenceException(); + + RuntimeType rtType = (RuntimeType)Type.GetTypeFromHandle(type)!; + + if (rtType.IsByRefLike) + throw new NotSupportedException(SR.NotSupported_ByRefLike); + + if (rtType.IsPointer || rtType.IsFunctionPointer) + throw new NotSupportedException(SR.NotSupported_BoxedPointer); + + + if (rtType.IsValueType) + { + object? result = null; + InternalBox(new QCallTypeHandle(ref rtType), ref target, ObjectHandleOnStack.Create(ref result)); + return result; + } + else + { + return Unsafe.As(ref target); + } + } } } diff --git a/src/mono/mono/metadata/icall-def.h b/src/mono/mono/metadata/icall-def.h index 84bb3df63a8cd7..766754d520080e 100644 --- a/src/mono/mono/metadata/icall-def.h +++ b/src/mono/mono/metadata/icall-def.h @@ -435,6 +435,7 @@ HANDLES(RUNH_1, "GetObjectValue", ves_icall_System_Runtime_CompilerServices_Runt HANDLES(RUNH_6, "GetSpanDataFrom", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetSpanDataFrom, gpointer, 3, (MonoClassField_ptr, MonoType_ptr, gpointer)) HANDLES(RUNH_2, "GetUninitializedObjectInternal", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetUninitializedObjectInternal, MonoObject, 1, (MonoType_ptr)) HANDLES(RUNH_3, "InitializeArray", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray, void, 2, (MonoArray, MonoClassField_ptr)) +HANDLES(RUNH_8, "InternalBox", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InternalBox, void, 3, (MonoQCallTypeHandle, char_ref, MonoObjectHandleOnStack)) HANDLES(RUNH_7, "InternalGetHashCode", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InternalGetHashCode, int, 1, (MonoObject)) 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)) diff --git a/src/mono/mono/metadata/icall-table.h b/src/mono/mono/metadata/icall-table.h index 1336ed368e9d24..6af12c82e4f4c1 100644 --- a/src/mono/mono/metadata/icall-table.h +++ b/src/mono/mono/metadata/icall-table.h @@ -71,6 +71,7 @@ typedef mono_unichar2 *mono_unichar2_ptr; typedef mono_unichar4 *mono_unichar4_ptr; typedef MonoSpanOfObjects *MonoSpanOfObjects_ref; +typedef char *char_ref; typedef char **char_ptr_ref; typedef gint32 *gint32_ref; typedef gint64 *gint64_ref; @@ -173,6 +174,7 @@ typedef MonoStringHandle MonoStringOutHandle; #define MONO_HANDLE_TYPE_WRAP_int_ref ICALL_HANDLES_WRAP_VALUETYPE_REF #define MONO_HANDLE_TYPE_WRAP_gint32_ref ICALL_HANDLES_WRAP_VALUETYPE_REF #define MONO_HANDLE_TYPE_WRAP_int_ptr_ref ICALL_HANDLES_WRAP_VALUETYPE_REF +#define MONO_HANDLE_TYPE_WRAP_char_ref ICALL_HANDLES_WRAP_VALUETYPE_REF #define MONO_HANDLE_TYPE_WRAP_char_ptr_ref ICALL_HANDLES_WRAP_VALUETYPE_REF #define MONO_HANDLE_TYPE_WRAP_guint8_ptr_ref ICALL_HANDLES_WRAP_VALUETYPE_REF #define MONO_HANDLE_TYPE_WRAP_MonoResolveTokenError_ref ICALL_HANDLES_WRAP_VALUETYPE_REF diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 6a3e33bc341af6..54cae6cef38d23 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -1210,6 +1210,26 @@ ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_PrepareMethod (MonoMeth // FIXME: Implement } +void +ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_InternalBox (MonoQCallTypeHandle type_handle, char* data, MonoObjectHandleOnStack obj, MonoError *error) +{ + MonoType *type = type_handle.type; + MonoClass *klass = mono_class_from_mono_type_internal (type); + + g_assert (m_class_is_valuetype (klass)); + + mono_class_init_checked (klass, error); + goto_if_nok (error, error_ret); + + MonoObject* raw_obj = mono_value_box_checked (klass, data, error); + goto_if_nok (error, error_ret); + + HANDLE_ON_STACK_SET(obj, raw_obj); + return; +error_ret: + HANDLE_ON_STACK_SET (obj, NULL); +} + MonoObjectHandle ves_icall_System_Object_MemberwiseClone (MonoObjectHandle this_obj, MonoError *error) { From b285704f83cd852ca9b7df1a42a9c79042c35745 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 3 Apr 2024 18:38:37 +0000 Subject: [PATCH 02/12] PR feedback --- .../RuntimeHelpers.CoreCLR.cs | 17 ++++++----- .../src/System/RuntimeHandles.cs | 5 ++++ .../src/System/Runtime/RuntimeExports.cs | 4 +++ .../RuntimeHelpers.NativeAot.cs | 19 ++++++------ .../src/System/TypedReference.cs | 3 ++ .../src/Resources/Strings.resx | 3 -- .../CompilerServices/RuntimeHelpersTests.cs | 30 +++++++++++++++++-- .../CompilerServices/RuntimeHelpers.Mono.cs | 23 +++++++------- 8 files changed, 69 insertions(+), 35 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 0e716f1d70b1ae..4696427fa0d26d 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 @@ -422,18 +422,19 @@ private static unsafe void DispatchTailCalls( if (type.IsNullHandle()) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); - RuntimeType rtType = (RuntimeType)Type.GetTypeFromHandle(type)!; + TypeHandle handle = type.GetNativeTypeHandle(); - if (rtType.IsByRefLike) - throw new NotSupportedException(SR.NotSupported_ByRefLike); + if (handle.IsTypeDesc) + throw new ArgumentException(SR.Arg_TypeNotSupported); - if (rtType.IsPointer || rtType.IsFunctionPointer) - throw new NotSupportedException(SR.NotSupported_BoxedPointer); + MethodTable* pMT = handle.AsMethodTable(); - if (rtType.IsActualValueType) + if (pMT->IsValueType) { - object? result = Box(rtType.GetNativeTypeHandle().AsMethodTable(), ref target); - GC.KeepAlive(rtType); + if (pMT->IsByRefLike) + throw new NotSupportedException(SR.NotSupported_ByRefLike); + object? result = Box(pMT, ref target); + GC.KeepAlive(type); return result; } else 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); diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs index 378fabf56836f0..522f4baf9901ab 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs @@ -75,6 +75,10 @@ public static unsafe object RhNewArray(MethodTable* pEEType, int length) [RuntimeExport("RhBox")] public static unsafe object RhBox(MethodTable* pEEType, ref byte data) { + // Compatibility with CoreCLR, throw on a null reference to the unboxed data. + if (Unsafe.IsNullRef(ref target)) + throw new NullReferenceException(); + ref byte dataAdjustedForNullable = ref data; // Can box non-ByRefLike value types only (which also implies no finalizers). 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 bf5405ababa4db..b3a970294ce54a 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,24 +362,25 @@ public static unsafe object GetUninitializedObject( return RuntimeImports.RhNewObject(mt); } - public static unsafe object Box(ref byte target, RuntimeTypeHandle type) + public static unsafe object? Box(ref byte target, RuntimeTypeHandle type) { if (type.IsNull) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); - // Compatibility with CoreCLR, throw on a null reference to the unboxed data. - if (Unsafe.IsNullRef(ref target)) - throw new NullReferenceException(); - MethodTable* eeType = type.ToMethodTable(); + if (eeType->IsByRef || eeType->IsPointer || eeType->IsFunctionPointer) + throw new ArgumentException(SR.Arg_TypeNotSupported); + + if (!eeType->IsValueType) + { + return Unsafe.As>(ref target); + } + if (eeType->IsByRefLike) throw new NotSupportedException(SR.NotSupported_ByRefLike); - if (eeType->IsPointer || eeType->IsFunctionPointer) - throw new NotSupportedException(SR.NotSupported_BoxedPointer); - - return RuntimeImports.RhBoxAny(ref target, eeType); + return RuntimeImports.RhBox(ref target, eeType); } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypedReference.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypedReference.cs index 0ab8ffa1ceee3e..56e8ef05dd1cd3 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypedReference.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypedReference.cs @@ -52,6 +52,9 @@ public static unsafe object ToObject(TypedReference value) { RuntimeTypeHandle handle = RawTargetTypeToken(value); + if (handle.IsNull) + ThrowHelper.ThrowArgumentException_ArgumentNull_TypedRefType(); + MethodTable* mt = handle.ToMethodTable(); if (mt->IsPointer || mt->IsFunctionPointer) { diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 4a6711689f416b..bbc4b27c51ddeb 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -2954,9 +2954,6 @@ Assembly.LoadFrom with hashValue is not supported. - - Cannot create boxed pointer values. - Cannot create boxed ByRef-like values. 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 e93acdd87abae5..a84c0d9c063a76 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 @@ -449,7 +449,7 @@ public static void BoxPrimitive() [Fact] public static void BoxPointer() { - Assert.Throws(() => + Assert.Throws(() => { nint value = 3; object result = RuntimeHelpers.Box(ref Unsafe.As(ref value), typeof(void*).TypeHandle); @@ -519,6 +519,14 @@ public static void BoxNullable() Assert.Equal(value, Assert.IsType(result)); } + [Fact] + public static void BoxNullNullable() + { + float? value = null; + object? result = RuntimeHelpers.Box(ref Unsafe.As(ref value), typeof(float?).TypeHandle); + Assert.Null(result); + } + [Fact] public static void NullBox() { @@ -526,7 +534,7 @@ public static void NullBox() } [Fact] - public static void BoxInvalidType() + public static void BoxNullTypeHandle() { Assert.Throws(() => { @@ -541,6 +549,24 @@ public static void BoxReferenceType() string str = "ABC"; Assert.Same(str, RuntimeHelpers.Box(ref Unsafe.As(ref str), typeof(string).TypeHandle)); } + + [Fact] + public static void BoxArrayType() + { + string[] arr = ["a", "b", "c"]; + Assert.Same(arr, RuntimeHelpers.Box(ref Unsafe.As(ref arr), typeof(string[]).TypeHandle)); + } + + [Fact] + public static void BoxGenericParameterType() + { + Type t = typeof(List<>).GetGenericArguments()[0]; + Assert.Throws(() => + { + byte value = 3; + RuntimeHelpers.Box(ref value, t.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 f313891e955676..2c32e903d20d15 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 @@ -226,23 +226,20 @@ private static extern unsafe IntPtr GetSpanDataFrom( RuntimeType rtType = (RuntimeType)Type.GetTypeFromHandle(type)!; - if (rtType.IsByRefLike) - throw new NotSupportedException(SR.NotSupported_ByRefLike); - - if (rtType.IsPointer || rtType.IsFunctionPointer) - throw new NotSupportedException(SR.NotSupported_BoxedPointer); + if (rtType.IsPointer || rtType.IsFunctionPointer || rtType.IsByRef || rtType.IsGenericParameter) + throw new ArgumentException(SR.Arg_TypeNotSupported); - - if (rtType.IsValueType) - { - object? result = null; - InternalBox(new QCallTypeHandle(ref rtType), ref target, ObjectHandleOnStack.Create(ref result)); - return result; - } - else + if (!rtType.IsValueType) { return Unsafe.As(ref target); } + + if (rtType.IsByRefLike) + throw new NotSupportedException(SR.NotSupported_ByRefLike); + + object? result = null; + InternalBox(new QCallTypeHandle(ref rtType), ref target, ObjectHandleOnStack.Create(ref result)); + return result; } } } From a50ac34fd854bafa6b84b6ee7ecefa318e67a3e1 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 3 Apr 2024 13:20:47 -0700 Subject: [PATCH 03/12] Update src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs --- .../Runtime.Base/src/System/Runtime/RuntimeExports.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs index 522f4baf9901ab..4ee9fcf8f33767 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs @@ -75,8 +75,8 @@ public static unsafe object RhNewArray(MethodTable* pEEType, int length) [RuntimeExport("RhBox")] public static unsafe object RhBox(MethodTable* pEEType, ref byte data) { - // Compatibility with CoreCLR, throw on a null reference to the unboxed data. - if (Unsafe.IsNullRef(ref target)) + // A null can be passed for boxing of a null ref. + if (Unsafe.IsNullRef(ref data)) throw new NullReferenceException(); ref byte dataAdjustedForNullable = ref data; From 06f4e60147b1b4fdb3b101a2edf17ed9799d6b72 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 3 Apr 2024 14:01:21 -0700 Subject: [PATCH 04/12] Remove stray token --- .../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 b3a970294ce54a..4e34868199c636 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 @@ -374,7 +374,7 @@ public static unsafe object GetUninitializedObject( if (!eeType->IsValueType) { - return Unsafe.As>(ref target); + return Unsafe.As(ref target); } if (eeType->IsByRefLike) From db96ac7bcf43a186506dc58809e31e5f2a8845f7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 3 Apr 2024 15:12:33 -0700 Subject: [PATCH 05/12] Update RuntimeExports.cs Co-authored-by: Jan Kotas --- .../Runtime.Base/src/System/Runtime/RuntimeExports.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs index 4ee9fcf8f33767..6fa6c5460dbb83 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs @@ -76,8 +76,7 @@ public static unsafe object RhNewArray(MethodTable* pEEType, int length) public static unsafe object RhBox(MethodTable* pEEType, ref byte data) { // A null can be passed for boxing of a null ref. - if (Unsafe.IsNullRef(ref data)) - throw new NullReferenceException(); + _ = Unsafe.ReadUnaligned(ref data); ref byte dataAdjustedForNullable = ref data; From 6f54fb504fcfa21f950dfe01fcbf71dd6a6dd0a9 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 3 Apr 2024 23:31:58 +0000 Subject: [PATCH 06/12] RhBoxAny and RhBox have alternate-order argument lists. --- .../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 4e34868199c636..b124f56f8da775 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 @@ -380,7 +380,7 @@ public static unsafe object GetUninitializedObject( if (eeType->IsByRefLike) throw new NotSupportedException(SR.NotSupported_ByRefLike); - return RuntimeImports.RhBox(ref target, eeType); + return RuntimeImports.RhBox(eeType, ref target); } } From e69e057171cd399fd5ee76fca3edc4c0079a4412 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 3 Apr 2024 23:59:29 +0000 Subject: [PATCH 07/12] Fix formatting --- .../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 b124f56f8da775..23d21a57eb24d4 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 @@ -376,7 +376,7 @@ public static unsafe object GetUninitializedObject( { return Unsafe.As(ref target); } - + if (eeType->IsByRefLike) throw new NotSupportedException(SR.NotSupported_ByRefLike); From bae16934b56be11194f9aa35112286a6df962c6c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 4 Apr 2024 12:15:54 -0700 Subject: [PATCH 08/12] Add tests for void and generic type definitions and other feedback --- .../RuntimeHelpers.CoreCLR.cs | 7 +++++++ .../System/Runtime/CompilerServices/Unsafe.cs | 10 ++++++++++ .../RuntimeHelpers.NativeAot.cs | 18 ++++++++++++----- .../CompilerServices/RuntimeHelpersTests.cs | 20 +++++++++++++++++++ .../CompilerServices/RuntimeHelpers.Mono.cs | 9 ++++++++- 5 files changed, 58 insertions(+), 6 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 4696427fa0d26d..5fab1972d96cde 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 @@ -429,10 +429,17 @@ private static unsafe void DispatchTailCalls( MethodTable* pMT = handle.AsMethodTable(); + if (pMT->IsGenericTypeDefinition) + throw new ArgumentException(SR.Arg_TypeNotSupported); + if (pMT->IsValueType) { if (pMT->IsByRefLike) throw new NotSupportedException(SR.NotSupported_ByRefLike); + + if (MethodTable.AreSameType(pMT, (MethodTable*)RuntimeTypeHandle.ToIntPtr(typeof(void).TypeHandle))) + throw new ArgumentException(SR.Arg_TypeNotSupported); + object? result = Box(pMT, ref target); GC.KeepAlive(type); return result; diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/Unsafe.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/Unsafe.cs index 341cd832604715..89eedc1638a723 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/Unsafe.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/Unsafe.cs @@ -120,6 +120,16 @@ public static T ReadUnaligned(void* source) throw new PlatformNotSupportedException(); } + /// + /// Reads a value of type from the given location. + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T ReadUnaligned(ref readonly byte source) + { + throw new PlatformNotSupportedException(); + } + /// /// Copies bytes from the source address to the destination address. /// 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 23d21a57eb24d4..54e8c87fce59e7 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 @@ -367,20 +367,28 @@ public static unsafe object GetUninitializedObject( if (type.IsNull) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type); - MethodTable* eeType = type.ToMethodTable(); + MethodTable* mt = type.ToMethodTable(); - if (eeType->IsByRef || eeType->IsPointer || eeType->IsFunctionPointer) + if (mt->ElementType == EETypeElementType.Void || mt->IsGenericTypeDefinition || mt->IsByRef || mt->IsPointer || mt->IsFunctionPointer) throw new ArgumentException(SR.Arg_TypeNotSupported); - if (!eeType->IsValueType) + if (mt->NumVtableSlots == 0) + { + // This is a type without a vtable or GCDesc. We must not allow creating an instance of it + throw ReflectionCoreExecution.ExecutionEnvironment.CreateMissingMetadataException(Type.GetTypeFromHandle(type)); + } + // Paranoid check: not-meant-for-GC-heap types should be reliably identifiable by empty vtable. + Debug.Assert(!mt->ContainsGCPointers || RuntimeImports.RhGetGCDescSize(mt) != 0); + + if (!mt->IsValueType) { return Unsafe.As(ref target); } - if (eeType->IsByRefLike) + if (mt->IsByRefLike) throw new NotSupportedException(SR.NotSupported_ByRefLike); - return RuntimeImports.RhBox(eeType, ref target); + return RuntimeImports.RhBox(mt, ref target); } } 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 a84c0d9c063a76..8e4906bfc85828 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 @@ -567,6 +567,26 @@ public static void BoxGenericParameterType() RuntimeHelpers.Box(ref value, t.TypeHandle); }); } + + [Fact] + public static void BoxGenericTypeDefinition() + { + Assert.Throws(() => + { + byte value = 3; + RuntimeHelpers.Box(ref value, typeof(List<>).TypeHandle); + }); + } + + [Fact] + public static void BoxVoid() + { + Assert.Throws(() => + { + byte value = 3; + RuntimeHelpers.Box(ref value, typeof(void).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 2c32e903d20d15..5030c1674926ad 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 @@ -226,8 +226,15 @@ private static extern unsafe IntPtr GetSpanDataFrom( RuntimeType rtType = (RuntimeType)Type.GetTypeFromHandle(type)!; - if (rtType.IsPointer || rtType.IsFunctionPointer || rtType.IsByRef || rtType.IsGenericParameter) + if (rtType.IsGenericTypeDefinition + || rtType.IsPointer + || rtType.IsFunctionPointer + || rtType.IsByRef + || rtType.IsGenericParameter + || rtType == typeof(void)) + { throw new ArgumentException(SR.Arg_TypeNotSupported); + } if (!rtType.IsValueType) { From d9e94dd620a77e3e54d4304df3a9f1a767246004 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 4 Apr 2024 16:24:19 -0700 Subject: [PATCH 09/12] Skip test case on NativeAot --- .../System/Runtime/CompilerServices/RuntimeHelpersTests.cs | 5 ++++- 1 file changed, 4 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 8e4906bfc85828..76e1baec7551fe 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 @@ -557,7 +557,10 @@ public static void BoxArrayType() Assert.Same(arr, RuntimeHelpers.Box(ref Unsafe.As(ref arr), typeof(string[]).TypeHandle)); } - [Fact] + // 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 BoxGenericParameterType() { Type t = typeof(List<>).GetGenericArguments()[0]; From 442be13c426faa9e7b04bfebf882d6205a7a741d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 5 Apr 2024 10:06:59 -0700 Subject: [PATCH 10/12] Add test for partially open generics --- .../CompilerServices/RuntimeHelpers.CoreCLR.cs | 5 ++++- .../CompilerServices/RuntimeHelpersTests.cs | 14 ++++++++++++++ .../CompilerServices/RuntimeHelpers.Mono.cs | 2 +- 3 files changed, 19 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 5fab1972d96cde..86e38d6c233368 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 @@ -429,7 +429,7 @@ private static unsafe void DispatchTailCalls( MethodTable* pMT = handle.AsMethodTable(); - if (pMT->IsGenericTypeDefinition) + if (pMT->ContainsGenericVariables) throw new ArgumentException(SR.Arg_TypeNotSupported); if (pMT->IsValueType) @@ -556,6 +556,7 @@ internal unsafe struct MethodTable // WFLAGS_HIGH_ENUM private const uint enum_flag_ContainsPointers = 0x01000000; + private const uint enum_flag_ContainsGenericVariables = 0x20000000; private const uint enum_flag_HasComponentSize = 0x80000000; private const uint enum_flag_HasTypeEquivalence = 0x02000000; private const uint enum_flag_Category_Mask = 0x000F0000; @@ -663,6 +664,8 @@ public bool IsConstructedGenericType } } + public bool ContainsGenericVariables => (Flags & enum_flag_ContainsGenericVariables) != 0; + /// /// Gets a for the element type of the current type. /// 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 76e1baec7551fe..afb72bbe0949fb 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 @@ -571,6 +571,20 @@ public static void BoxGenericParameterType() }); } + // We can't even get a RuntimeTypeHandle for a partially instantiated 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 BoxPartiallyOpenGeneric() + { + Type t = typeof(Dictionary<,>).MakeGenericType(typeof(object), typeof(Dictionary<,>).GetGenericArguments()[1]); + Assert.Throws(() => + { + byte value = 3; + RuntimeHelpers.Box(ref value, t.TypeHandle); + }); + } + [Fact] public static void BoxGenericTypeDefinition() { 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 5030c1674926ad..a0e62183f59559 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 @@ -226,7 +226,7 @@ private static extern unsafe IntPtr GetSpanDataFrom( RuntimeType rtType = (RuntimeType)Type.GetTypeFromHandle(type)!; - if (rtType.IsGenericTypeDefinition + if (rtType.ContainsGenericParameters || rtType.IsPointer || rtType.IsFunctionPointer || rtType.IsByRef From f7df0bfd3cf906ab1a996b4ab514e5d6da924916 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 5 Apr 2024 10:12:09 -0700 Subject: [PATCH 11/12] 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 86e38d6c233368..69506c2feda11f 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 @@ -417,6 +417,15 @@ private static unsafe void DispatchTailCalls( } } + /// + /// Create a boxed object of the specified type from the data located at the target reference. + /// + /// The target data + /// The type of box to create. + /// A boxed object containing the specified data. + /// The specified type handle is null. + /// The specified type cannot have a boxed instance of itself created. + /// The passed in type is a by-ref-like type. public static unsafe object? Box(ref byte target, 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 54e8c87fce59e7..c790682ecfcbc8 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); } + /// + /// Create a boxed object of the specified type from the data located at the target reference. + /// + /// The target data + /// The type of box to create. + /// A boxed object containing the specified data. + /// The specified type handle is null. + /// The specified type cannot have a boxed instance of itself created. + /// The passed in type is a by-ref-like type. public static unsafe object? Box(ref byte target, 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 a0e62183f59559..c112a1263e241c 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 void InternalBox(QCallTypeHandle type, ref byte target, ObjectHandleOnStack result); + /// + /// Create a boxed object of the specified type from the data located at the target reference. + /// + /// The target data + /// The type of box to create. + /// A boxed object containing the specified data. + /// The specified type handle is null. + /// The specified type cannot have a boxed instance of itself created. + /// The passed in type is a by-ref-like type. public static object? Box(ref byte target, RuntimeTypeHandle type) { if (type.Value is 0) From 3e43be5f7a7bd6db9be314d84eb8e3b4cfc2bcda Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 5 Apr 2024 15:17:41 -0700 Subject: [PATCH 12/12] Add a remark about the box IL instruction --- .../src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs | 1 + 1 file changed, 1 insertion(+) 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 c112a1263e241c..1e2241de12b35c 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 @@ -224,6 +224,7 @@ private static extern unsafe IntPtr GetSpanDataFrom( /// The specified type handle is null. /// The specified type cannot have a boxed instance of itself created. /// The passed in type is a by-ref-like type. + /// This returns an object that is equivalent to executing the IL box instruction with the provided target address and type. public static object? Box(ref byte target, RuntimeTypeHandle type) { if (type.Value is 0)