Skip to content

Commit 2d2d59c

Browse files
huoyaoyuanAaronRobinsonMSFTjkotas
authored
Convert some more array FCalls to managed (#102739)
* Convert InternalSetValue to managed * Implement InitializeArray in managed * Implement GetSpanDataFrom in managed * Remove FCall for GetCorElementTypeOfElementType * Move PrimitiveWiden to InvokeUtils * Apply suggestions from code review Co-authored-by: Aaron Robinson <arobins@microsoft.com> Co-authored-by: Jan Kotas <jkotas@microsoft.com>
1 parent f920dac commit 2d2d59c

File tree

21 files changed

+497
-432
lines changed

21 files changed

+497
-432
lines changed

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

Lines changed: 102 additions & 148 deletions
Large diffs are not rendered by default.

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

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

17-
[MethodImpl(MethodImplOptions.InternalCall)]
18-
private static extern unsafe CorElementType InternalGetCorElementType(MethodTable* pMT);
19-
2017
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2118
private static unsafe CorElementType InternalGetCorElementType(RuntimeType rt)
2219
{
2320
Debug.Assert(rt.IsActualEnum);
24-
CorElementType elementType = InternalGetCorElementType((MethodTable*)rt.GetUnderlyingNativeHandle());
21+
CorElementType elementType = rt.GetNativeTypeHandle().AsMethodTable()->GetPrimitiveCorElementType();
2522
GC.KeepAlive(rt);
2623
return elementType;
2724
}
2825

2926
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3027
private unsafe CorElementType InternalGetCorElementType()
3128
{
32-
CorElementType elementType = InternalGetCorElementType(RuntimeHelpers.GetMethodTable(this));
29+
CorElementType elementType = RuntimeHelpers.GetMethodTable(this)->GetPrimitiveCorElementType();
3330
GC.KeepAlive(this);
3431
return elementType;
3532
}
@@ -71,7 +68,7 @@ internal static unsafe RuntimeType InternalGetUnderlyingType(RuntimeType enumTyp
7168
// Sanity check the last element in the table
7269
Debug.Assert(s_underlyingTypes[(int)CorElementType.ELEMENT_TYPE_U] == typeof(nuint));
7370

74-
RuntimeType? underlyingType = s_underlyingTypes[(int)InternalGetCorElementType((MethodTable*)enumType.GetUnderlyingNativeHandle())];
71+
RuntimeType? underlyingType = s_underlyingTypes[(int)enumType.GetNativeTypeHandle().AsMethodTable()->GetPrimitiveCorElementType()];
7572
GC.KeepAlive(enumType);
7673

7774
Debug.Assert(underlyingType != null);

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ internal static unsafe class CastHelpers
2929
// Unlike the IsInstanceOfInterface and IsInstanceOfClass functions,
3030
// this test must deal with all kinds of type tests
3131
[DebuggerHidden]
32-
private static object? IsInstanceOfAny(void* toTypeHnd, object? obj)
32+
internal static object? IsInstanceOfAny(void* toTypeHnd, object? obj)
3333
{
3434
if (obj != null)
3535
{

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs

Lines changed: 114 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,126 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Buffers.Binary;
45
using System.Diagnostics;
56
using System.Diagnostics.CodeAnalysis;
67
using System.Reflection;
7-
using System.Runtime.CompilerServices;
88
using System.Runtime.InteropServices;
99
using System.Runtime.Serialization;
1010
using System.Runtime.Versioning;
11-
using System.Threading;
1211

1312
namespace System.Runtime.CompilerServices
1413
{
1514
public static partial class RuntimeHelpers
1615
{
1716
[Intrinsic]
18-
[MethodImpl(MethodImplOptions.InternalCall)]
19-
public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle);
17+
public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHandle)
18+
{
19+
if (array is null)
20+
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
2021

21-
[MethodImpl(MethodImplOptions.InternalCall)]
22-
private static extern unsafe void* GetSpanDataFrom(
22+
if (fldHandle.IsNullHandle())
23+
throw new ArgumentException(SR.Argument_InvalidHandle);
24+
25+
IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo();
26+
27+
if (!RuntimeFieldHandle.GetRVAFieldInfo(fldInfo.Value, out void* address, out uint size))
28+
throw new ArgumentException(SR.Argument_BadFieldForInitializeArray);
29+
30+
// Note that we do not check that the field is actually in the PE file that is initializing
31+
// the array. Basically, the data being published can be accessed by anyone with the proper
32+
// permissions (C# marks these as assembly visibility, and thus are protected from outside
33+
// snooping)
34+
35+
MethodTable* pMT = GetMethodTable(array);
36+
TypeHandle elementTH = pMT->GetArrayElementTypeHandle();
37+
38+
if (elementTH.IsTypeDesc || !elementTH.AsMethodTable()->IsPrimitive) // Enum is included
39+
throw new ArgumentException(SR.Argument_BadArrayForInitializeArray);
40+
41+
nuint totalSize = pMT->ComponentSize * array.NativeLength;
42+
43+
// make certain you don't go off the end of the rva static
44+
if (totalSize > size)
45+
throw new ArgumentException(SR.Argument_BadFieldForInitializeArray);
46+
47+
ref byte src = ref *(byte*)address; // Ref is extending the lifetime of the static field.
48+
GC.KeepAlive(fldInfo);
49+
50+
ref byte dst = ref MemoryMarshal.GetArrayDataReference(array);
51+
52+
Debug.Assert(!elementTH.AsMethodTable()->ContainsGCPointers);
53+
54+
if (BitConverter.IsLittleEndian)
55+
{
56+
SpanHelpers.Memmove(ref dst, ref src, totalSize);
57+
}
58+
else
59+
{
60+
switch (pMT->ComponentSize)
61+
{
62+
case sizeof(byte):
63+
SpanHelpers.Memmove(ref dst, ref src, totalSize);
64+
break;
65+
case sizeof(ushort):
66+
BinaryPrimitives.ReverseEndianness(
67+
new ReadOnlySpan<ushort>(ref Unsafe.As<byte, ushort>(ref src), array.Length),
68+
new Span<ushort>(ref Unsafe.As<byte, ushort>(ref dst), array.Length));
69+
break;
70+
case sizeof(uint):
71+
BinaryPrimitives.ReverseEndianness(
72+
new ReadOnlySpan<uint>(ref Unsafe.As<byte, uint>(ref src), array.Length),
73+
new Span<uint>(ref Unsafe.As<byte, uint>(ref dst), array.Length));
74+
break;
75+
case sizeof(ulong):
76+
BinaryPrimitives.ReverseEndianness(
77+
new ReadOnlySpan<ulong>(ref Unsafe.As<byte, ulong>(ref src), array.Length),
78+
new Span<ulong>(ref Unsafe.As<byte, ulong>(ref dst), array.Length));
79+
break;
80+
default:
81+
Debug.Fail("Incorrect primitive type size!");
82+
break;
83+
}
84+
}
85+
}
86+
87+
private static unsafe ref byte GetSpanDataFrom(
2388
RuntimeFieldHandle fldHandle,
2489
RuntimeTypeHandle targetTypeHandle,
25-
out int count);
90+
out int count)
91+
{
92+
if (fldHandle.IsNullHandle())
93+
throw new ArgumentException(SR.Argument_InvalidHandle);
94+
95+
IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo();
96+
97+
if (!RuntimeFieldHandle.GetRVAFieldInfo(fldInfo.Value, out void* data, out uint totalSize))
98+
throw new ArgumentException(SR.Argument_BadFieldForInitializeArray);
99+
100+
TypeHandle th = targetTypeHandle.GetNativeTypeHandle();
101+
Debug.Assert(!th.IsTypeDesc); // TypeDesc can't be used as generic parameter
102+
MethodTable* targetMT = th.AsMethodTable();
103+
104+
if (!targetMT->IsPrimitive) // Enum is included
105+
throw new ArgumentException(SR.Argument_BadArrayForInitializeArray);
106+
107+
uint targetTypeSize = targetMT->GetNumInstanceFieldBytes();
108+
Debug.Assert(uint.IsPow2(targetTypeSize));
109+
110+
if (((nuint)data & (targetTypeSize - 1)) != 0)
111+
throw new ArgumentException(SR.Argument_BadFieldForInitializeArray);
112+
113+
if (!BitConverter.IsLittleEndian)
114+
{
115+
throw new PlatformNotSupportedException();
116+
}
117+
118+
count = (int)(totalSize / targetTypeSize);
119+
ref byte dataRef = ref *(byte*)data; // Ref is extending the lifetime of the static field.
120+
GC.KeepAlive(fldInfo);
121+
122+
return ref dataRef;
123+
}
26124

27125
// GetObjectValue is intended to allow value classes to be manipulated as 'Object'
28126
// but have aliasing behavior of a value class. The intent is that you would use
@@ -655,6 +753,8 @@ public int MultiDimensionalArrayRank
655753
// Warning! UNLIKE the similarly named Reflection api, this method also returns "true" for Enums.
656754
public bool IsPrimitive => (Flags & enum_flag_Category_Mask) is enum_flag_Category_PrimitiveValueType or enum_flag_Category_TruePrimitive;
657755

756+
public bool IsTruePrimitive => (Flags & enum_flag_Category_Mask) is enum_flag_Category_TruePrimitive;
757+
658758
public bool HasInstantiation => (Flags & enum_flag_HasComponentSize) == 0 && (Flags & enum_flag_GenericsMask) != enum_flag_GenericsMask_NonGeneric;
659759

660760
public bool IsGenericTypeDefinition => (Flags & (enum_flag_HasComponentSize | enum_flag_GenericsMask)) == enum_flag_GenericsMask_TypicalInst;
@@ -684,6 +784,13 @@ public TypeHandle GetArrayElementTypeHandle()
684784

685785
[MethodImpl(MethodImplOptions.InternalCall)]
686786
public extern uint GetNumInstanceFieldBytes();
787+
788+
/// <summary>
789+
/// Get the <see cref="CorElementType"/> representing primitive-like type. Enums are represented by underlying type.
790+
/// </summary>
791+
/// <remarks>This method should only be called when <see cref="IsPrimitive"/> returns <see langword="true"/>.</remarks>
792+
[MethodImpl(MethodImplOptions.InternalCall)]
793+
public extern CorElementType GetPrimitiveCorElementType();
687794
}
688795

689796
// Subset of src\vm\methodtable.h

src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1089,7 +1089,7 @@ public RuntimeFieldInfoStub(RuntimeFieldHandleInternal fieldHandle, object keepa
10891089
}
10901090

10911091
[NonVersionable]
1092-
public unsafe struct RuntimeFieldHandle : IEquatable<RuntimeFieldHandle>, ISerializable
1092+
public unsafe partial struct RuntimeFieldHandle : IEquatable<RuntimeFieldHandle>, ISerializable
10931093
{
10941094
// Returns handle for interop with EE. The handle is guaranteed to be non-null.
10951095
internal RuntimeFieldHandle GetNativeHandle()
@@ -1193,6 +1193,10 @@ internal static RuntimeType GetApproxDeclaringType(IRuntimeFieldInfo field)
11931193
[MethodImpl(MethodImplOptions.InternalCall)]
11941194
internal static extern IntPtr GetStaticFieldAddress(RtFieldInfo field);
11951195

1196+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeFieldHandle_GetRVAFieldInfo")]
1197+
[return: MarshalAs(UnmanagedType.Bool)]
1198+
internal static partial bool GetRVAFieldInfo(RuntimeFieldHandleInternal field, out void* address, out uint size);
1199+
11961200
[MethodImpl(MethodImplOptions.InternalCall)]
11971201
internal static extern int GetToken(RtFieldInfo field);
11981202

0 commit comments

Comments
 (0)