-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Move unboxing helpers to managed code #109135
Changes from 2 commits
c70a57c
f1258a4
02636f8
d8e01e3
b2c6991
12d17e5
3f281b9
8dfaedc
be624da
dec085f
e22038b
e44fab4
2964105
6469fbb
1e51dd2
ee6d42d
7761ad8
55e0df9
6a8d1ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Diagnostics; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace System.Runtime.CompilerServices | ||
{ | ||
[StackTraceHidden] | ||
[DebuggerStepThrough] | ||
internal static unsafe partial class BoxingHelpers | ||
{ | ||
[DebuggerHidden] | ||
private static unsafe void InitValueClass(ref byte destBytes, MethodTable *pMT) | ||
{ | ||
uint numInstanceFieldBytes = pMT->GetNumInstanceFieldBytes(); | ||
if (((uint)Unsafe.AsPointer(ref destBytes) | numInstanceFieldBytes & ((uint)sizeof(void*) - 1)) != 0) | ||
{ | ||
// If we have a non-pointer aligned instance field bytes count, or a non-aligned destBytes, we can zero out the data byte by byte | ||
// And we do not need to concern ourselves with references | ||
SpanHelpers.ClearWithoutReferences(ref destBytes, numInstanceFieldBytes); | ||
} | ||
else | ||
{ | ||
// Otherwise, use the helper which is safe for that situation | ||
SpanHelpers.ClearWithReferences(ref Unsafe.As<byte, IntPtr>(ref destBytes), (nuint)numInstanceFieldBytes / (nuint)sizeof(IntPtr)); | ||
} | ||
} | ||
|
||
[DebuggerHidden] | ||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
private static bool AreTypesEquivalent(MethodTable* pMTa, MethodTable* pMTb) | ||
{ | ||
if (pMTa == pMTb) | ||
{ | ||
return true; | ||
} | ||
|
||
if (pMTa->HasTypeEquivalence && pMTb->HasTypeEquivalence) | ||
{ | ||
return false; | ||
} | ||
|
||
return RuntimeHelpers.AreTypesEquivalent(pMTa, pMTb); | ||
} | ||
|
||
[DebuggerHidden] | ||
private static bool IsNullableForType(MethodTable* typeMT, MethodTable* boxedMT) | ||
{ | ||
if (!typeMT->IsNullable) | ||
{ | ||
return false; | ||
} | ||
|
||
MethodTable *pMTNullableArg = typeMT->InstantiationArg0(); | ||
if (pMTNullableArg == boxedMT) | ||
{ | ||
return true; | ||
} | ||
else | ||
{ | ||
return AreTypesEquivalent(pMTNullableArg, boxedMT); | ||
} | ||
} | ||
|
||
[DebuggerHidden] | ||
internal static void Unbox_Nullable(ref byte destPtr, MethodTable* typeMT, object? obj) | ||
{ | ||
if (obj == null) | ||
{ | ||
InitValueClass(ref destPtr, typeMT); | ||
} | ||
else | ||
{ | ||
if (!IsNullableForType(typeMT, RuntimeHelpers.GetMethodTable(obj))) | ||
{ | ||
// For safety's sake, also allow true nullables to be unboxed normally. | ||
// This should not happen normally, but we want to be robust | ||
if (typeMT == RuntimeHelpers.GetMethodTable(obj)) | ||
{ | ||
Unsafe.CopyBlockUnaligned(ref destPtr, ref RuntimeHelpers.GetRawData(obj), typeMT->GetNumInstanceFieldBytes()); | ||
return; | ||
} | ||
CastHelpers.ThrowInvalidCastException(obj, typeMT); | ||
} | ||
|
||
// Set the hasValue field on the Nullable type. It MUST always be placed at the start of the object. | ||
*(bool*)destPtr = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't look correct. The cast is casting the value of the reference into pointer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that seems wrong. I kept waffling between refs and pointers here. Thanks. |
||
ref byte destValuePtr = ref typeMT->GetNullableValueFieldReferenceAndSize(ref destPtr, out uint size); | ||
Unsafe.CopyBlockUnaligned(ref destValuePtr, ref RuntimeHelpers.GetRawData(obj), size); | ||
} | ||
} | ||
|
||
internal static ref byte Unbox_Helper(MethodTable* pMT1, object obj) | ||
{ | ||
// must be a value type | ||
Debug.Assert(pMT1->IsValueType); | ||
|
||
MethodTable* pMT2 = RuntimeHelpers.GetMethodTable(obj); | ||
if ((pMT1->IsPrimitive && pMT2->IsPrimitive && | ||
pMT1->GetPrimitiveCorElementType() == pMT2->GetPrimitiveCorElementType()) || | ||
AreTypesEquivalent(pMT1, pMT2)) | ||
{ | ||
return ref RuntimeHelpers.GetRawData(obj); | ||
} | ||
|
||
CastHelpers.ThrowInvalidCastException(obj, pMT1); | ||
return ref Unsafe.AsRef<byte>(null); | ||
} | ||
|
||
internal static void Unbox_TypeTest(MethodTable *pMT1, MethodTable *pMT2) | ||
{ | ||
if (pMT1 == pMT2 || | ||
(pMT1->IsPrimitive && pMT2->IsPrimitive && | ||
pMT1->GetPrimitiveCorElementType() == pMT2->GetPrimitiveCorElementType()) || | ||
AreTypesEquivalent(pMT1, pMT2)) | ||
{ | ||
return; | ||
} | ||
|
||
CastHelpers.ThrowInvalidCastException(pMT1, pMT2); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most of these types are going to be loaded on startup path. It feels a bit too fine grained to have separate type for the few unboxing helpers.
Would it be better for the unboxing helpers to live in CastHelpers? They are coupled anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, seems reasonable to me. I had thought I might be moving the allocation based helpers over too so this class wouldn't be so small, but honestly, just copying the assembly routines from NativeAOT seems like a better path for most of those.