Skip to content

Commit 0112e24

Browse files
committed
Add InitializeArray
1 parent 3e350ac commit 0112e24

10 files changed

Lines changed: 161 additions & 137 deletions

File tree

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

Lines changed: 94 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,115 @@
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+
IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo();
2020

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

27114
// GetObjectValue is intended to allow value classes to be manipulated as 'Object'
28115
// but have aliasing behavior of a value class. The intent is that you would use

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,7 +1085,7 @@ public RuntimeFieldInfoStub(RuntimeFieldHandleInternal fieldHandle, object keepa
10851085
}
10861086

10871087
[NonVersionable]
1088-
public unsafe struct RuntimeFieldHandle : IEquatable<RuntimeFieldHandle>, ISerializable
1088+
public unsafe partial struct RuntimeFieldHandle : IEquatable<RuntimeFieldHandle>, ISerializable
10891089
{
10901090
// Returns handle for interop with EE. The handle is guaranteed to be non-null.
10911091
internal RuntimeFieldHandle GetNativeHandle()
@@ -1187,7 +1187,10 @@ internal static RuntimeType GetApproxDeclaringType(IRuntimeFieldInfo field)
11871187
internal static extern int GetInstanceFieldOffset(RtFieldInfo field);
11881188

11891189
[MethodImpl(MethodImplOptions.InternalCall)]
1190-
internal static extern IntPtr GetStaticFieldAddress(RtFieldInfo field);
1190+
internal static extern IntPtr GetStaticFieldAddress(IRuntimeFieldInfo field);
1191+
1192+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeFieldHandle_GetFieldSize")]
1193+
internal static partial uint GetFieldSize(QCallFieldHandle field);
11911194

11921195
[MethodImpl(MethodImplOptions.InternalCall)]
11931196
internal static extern int GetToken(RtFieldInfo field);

src/coreclr/classlibnative/bcltype/arraynative.cpp

Lines changed: 0 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -441,116 +441,3 @@ FCIMPL3(void, ArrayNative::SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE
441441
}
442442
}
443443
FCIMPLEND
444-
445-
// This method will initialize an array from a TypeHandle to a field.
446-
447-
FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntimeFieldHandle structField)
448-
{
449-
FCALL_CONTRACT;
450-
451-
BASEARRAYREF arr = BASEARRAYREF(pArrayRef);
452-
REFLECTFIELDREF refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField));
453-
HELPER_METHOD_FRAME_BEGIN_2(arr, refField);
454-
455-
if ((arr == 0) || (refField == NULL))
456-
COMPlusThrow(kArgumentNullException);
457-
458-
FieldDesc* pField = (FieldDesc*) refField->GetField();
459-
460-
if (!pField->IsRVA())
461-
COMPlusThrow(kArgumentException);
462-
463-
// Note that we do not check that the field is actually in the PE file that is initializing
464-
// the array. Basically the data being published is can be accessed by anyone with the proper
465-
// permissions (C# marks these as assembly visibility, and thus are protected from outside
466-
// snooping)
467-
468-
if (!CorTypeInfo::IsPrimitiveType(arr->GetArrayElementType()) && !arr->GetArrayElementTypeHandle().IsEnum())
469-
COMPlusThrow(kArgumentException);
470-
471-
SIZE_T dwCompSize = arr->GetComponentSize();
472-
SIZE_T dwElemCnt = arr->GetNumComponents();
473-
SIZE_T dwTotalSize = dwCompSize * dwElemCnt;
474-
475-
DWORD size = pField->LoadSize();
476-
477-
// make certain you don't go off the end of the rva static
478-
if (dwTotalSize > size)
479-
COMPlusThrow(kArgumentException);
480-
481-
void *src = pField->GetStaticAddressHandle(NULL);
482-
void *dest = arr->GetDataPtr();
483-
484-
#if BIGENDIAN
485-
DWORD i;
486-
switch (dwCompSize) {
487-
case 1:
488-
memcpyNoGCRefs(dest, src, dwElemCnt);
489-
break;
490-
case 2:
491-
for (i = 0; i < dwElemCnt; i++)
492-
*((UINT16*)dest + i) = GET_UNALIGNED_VAL16((UINT16*)src + i);
493-
break;
494-
case 4:
495-
for (i = 0; i < dwElemCnt; i++)
496-
*((UINT32*)dest + i) = GET_UNALIGNED_VAL32((UINT32*)src + i);
497-
break;
498-
case 8:
499-
for (i = 0; i < dwElemCnt; i++)
500-
*((UINT64*)dest + i) = GET_UNALIGNED_VAL64((UINT64*)src + i);
501-
break;
502-
default:
503-
// should not reach here.
504-
UNREACHABLE_MSG("Incorrect primitive type size!");
505-
break;
506-
}
507-
#else
508-
memcpyNoGCRefs(dest, src, dwTotalSize);
509-
#endif
510-
511-
HELPER_METHOD_FRAME_END();
512-
}
513-
FCIMPLEND
514-
515-
FCIMPL3_VVI(void*, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count)
516-
{
517-
FCALL_CONTRACT;
518-
struct
519-
{
520-
REFLECTFIELDREF refField;
521-
REFLECTCLASSBASEREF refClass;
522-
} gc;
523-
gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField));
524-
gc.refClass = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(FCALL_RTH_TO_REFLECTCLASS(targetTypeUnsafe));
525-
void* data = NULL;
526-
HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
527-
528-
FieldDesc* pField = (FieldDesc*)gc.refField->GetField();
529-
530-
if (!pField->IsRVA())
531-
COMPlusThrow(kArgumentException);
532-
533-
TypeHandle targetTypeHandle = gc.refClass->GetType();
534-
if (!CorTypeInfo::IsPrimitiveType(targetTypeHandle.GetSignatureCorElementType()) && !targetTypeHandle.IsEnum())
535-
COMPlusThrow(kArgumentException);
536-
537-
DWORD totalSize = pField->LoadSize();
538-
DWORD targetTypeSize = targetTypeHandle.GetSize();
539-
540-
data = pField->GetStaticAddressHandle(NULL);
541-
_ASSERTE(data != NULL);
542-
_ASSERTE(count != NULL);
543-
544-
if (AlignUp((UINT_PTR)data, targetTypeSize) != (UINT_PTR)data)
545-
COMPlusThrow(kArgumentException);
546-
547-
*count = (INT32)totalSize / targetTypeSize;
548-
549-
#if BIGENDIAN
550-
COMPlusThrow(kPlatformNotSupportedException);
551-
#endif
552-
553-
HELPER_METHOD_FRAME_END();
554-
return data;
555-
}
556-
FCIMPLEND

src/coreclr/classlibnative/bcltype/arraynative.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,6 @@ class ArrayNative
3131
// This set of methods will set a value in an array
3232
static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex);
3333

34-
// This method will initialize an array from a TypeHandle
35-
// to a field.
36-
static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField);
37-
38-
// This method will acquire data to create a span from a TypeHandle
39-
// to a field.
40-
static FCDECL3_VVI(void*, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count);
4134
};
4235

4336
extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray);

src/coreclr/vm/ecalllist.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -439,8 +439,6 @@ FCFuncStart(gMonitorFuncs)
439439
FCFuncEnd()
440440

441441
FCFuncStart(gRuntimeHelpers)
442-
FCFuncElement("InitializeArray", ArrayNative::InitializeArray)
443-
FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom)
444442
FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate)
445443
FCFuncElement("GetHashCode", ObjectNative::GetHashCode)
446444
FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode)

src/coreclr/vm/qcall.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,24 @@ class QCall
293293
}
294294
};
295295

296+
struct FieldHandle
297+
{
298+
Object ** m_ppObject;
299+
FieldDesc * m_pField;
300+
301+
operator FieldDesc * ()
302+
{
303+
LIMITED_METHOD_CONTRACT;
304+
return m_pField;
305+
}
306+
307+
FieldDesc * operator -> ()
308+
{
309+
LIMITED_METHOD_CONTRACT;
310+
return m_pField;
311+
}
312+
};
313+
296314
struct LoaderAllocatorHandle
297315
{
298316
LoaderAllocator * m_pLoaderAllocator;

src/coreclr/vm/qcallentrypoints.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ static const Entry s_QCall[] =
132132
DllImportEntry(RuntimeModule_GetScopeName)
133133
DllImportEntry(RuntimeModule_GetFullyQualifiedName)
134134
DllImportEntry(RuntimeModule_GetTypes)
135+
DllImportEntry(RuntimeFieldHandle_GetFieldSize)
135136
DllImportEntry(StackFrame_GetMethodDescFromNativeIP)
136137
DllImportEntry(ModuleBuilder_GetStringConstant)
137138
DllImportEntry(ModuleBuilder_GetTypeRef)

src/coreclr/vm/reflectioninvocation.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,17 +1246,32 @@ FCIMPL1(void*, RuntimeFieldHandle::GetStaticFieldAddress, ReflectFieldObject *pF
12461246
// IsFastPathSupported needs to checked before calling this method.
12471247
_ASSERTE(IsFastPathSupportedHelper(pFieldDesc));
12481248

1249-
PTR_BYTE base = 0;
1250-
if (!pFieldDesc->IsRVA())
1249+
if (pFieldDesc->IsRVA())
12511250
{
1252-
// For RVA the base is ignored and offset is used.
1253-
base = pFieldDesc->GetBase();
1251+
Module* pModule = pFieldDesc->GetModule();
1252+
return pModule->GetRvaField(pFieldDesc->GetOffset());
1253+
}
1254+
else
1255+
{
1256+
PTR_BYTE base = pFieldDesc->GetBase();
1257+
return PTR_VOID(base + pFieldDesc->GetOffset());
12541258
}
1255-
1256-
return PTR_VOID(base + pFieldDesc->GetOffset());
12571259
}
12581260
FCIMPLEND
12591261

1262+
extern "C" UINT QCALLTYPE RuntimeFieldHandle_GetFieldSize(QCall::FieldHandle pField)
1263+
{
1264+
QCALL_CONTRACT;
1265+
1266+
UINT ret = 0;
1267+
1268+
BEGIN_QCALL;
1269+
ret = pField->LoadSize();
1270+
END_QCALL;
1271+
1272+
return ret;
1273+
}
1274+
12601275
extern "C" void QCALLTYPE ReflectionInvocation_CompileMethod(MethodDesc * pMD)
12611276
{
12621277
QCALL_CONTRACT;

src/coreclr/vm/runtimehandles.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ class RuntimeFieldHandle {
308308
static FCDECL1(FC_BOOL_RET, AcquiresContextFromThis, FieldDesc *pField);
309309
static FCDECL1(Object*, GetLoaderAllocator, FieldDesc *pField);
310310
};
311+
extern "C" UINT QCALLTYPE RuntimeFieldHandle_GetFieldSize(QCall::FieldHandle pField);
311312

312313
class ModuleHandle {
313314

src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,25 @@ internal QCallTypeHandle(ref RuntimeTypeHandle rth)
9595
_handle = rth.Value;
9696
}
9797
}
98+
99+
// Wraps IRuntimeFieldInfo into a handle. Used to pass IRuntimeFieldInfo to native code without letting it be collected
100+
internal unsafe ref struct QCallFieldHandle
101+
{
102+
private void* _ptr;
103+
private IntPtr _handle;
104+
105+
#if CORECLR
106+
internal QCallFieldHandle(ref IRuntimeFieldInfo field)
107+
{
108+
_ptr = Unsafe.AsPointer(ref field);
109+
_handle = field.Value.Value;
110+
}
111+
#endif
112+
113+
internal QCallFieldHandle(ref RuntimeFieldHandle rth)
114+
{
115+
_ptr = Unsafe.AsPointer(ref rth);
116+
_handle = rth.Value;
117+
}
118+
}
98119
}

0 commit comments

Comments
 (0)