Skip to content

Commit

Permalink
Add InitializeArray
Browse files Browse the repository at this point in the history
  • Loading branch information
huoyaoyuan committed Jun 3, 2024
1 parent 3e350ac commit 0112e24
Show file tree
Hide file tree
Showing 10 changed files with 161 additions and 137 deletions.
Original file line number Diff line number Diff line change
@@ -1,28 +1,115 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers.Binary;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Threading;

namespace System.Runtime.CompilerServices
{
public static partial class RuntimeHelpers
{
[Intrinsic]
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle);
public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHandle)
{
IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo();

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern unsafe void* GetSpanDataFrom(
if (array is null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);

if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0)
throw new ArgumentException(SR.Argument_BadFieldForInitializeArray);

// Note that we do not check that the field is actually in the PE file that is initializing
// the array. Basically the data being published is can be accessed by anyone with the proper
// permissions (C# marks these as assembly visibility, and thus are protected from outside
// snooping)

if (!array.GetCorElementTypeOfElementType().IsPrimitiveType()) // Enum is included
throw new ArgumentException(SR.Argument_MustBePrimitiveArray);

MethodTable* pMT = GetMethodTable(array);
nuint totalSize = pMT->ComponentSize * array.NativeLength;

uint size = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo));

// make certain you don't go off the end of the rva static
if (totalSize > size)
throw new ArgumentException(SR.Argument_BadFieldForInitializeArray);

void* src = (void*)RuntimeFieldHandle.GetStaticFieldAddress(fldInfo);
ref byte dst = ref MemoryMarshal.GetArrayDataReference(array);

Debug.Assert(!pMT->GetArrayElementTypeHandle().AsMethodTable()->ContainsGCPointers);

if (BitConverter.IsLittleEndian)
{
SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize);
}
else
{
switch (pMT->ComponentSize)
{
case 1:
SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize);
break;
case 2:
BinaryPrimitives.ReverseEndianness(
new ReadOnlySpan<ushort>(src, array.Length),
new Span<ushort>(ref Unsafe.As<byte, ushort>(ref dst), array.Length));
break;
case 4:
BinaryPrimitives.ReverseEndianness(
new ReadOnlySpan<uint>(src, array.Length),
new Span<uint>(ref Unsafe.As<byte, uint>(ref dst), array.Length));
break;
case 8:
BinaryPrimitives.ReverseEndianness(
new ReadOnlySpan<ulong>(src, array.Length),
new Span<ulong>(ref Unsafe.As<byte, ulong>(ref dst), array.Length));
break;
default:
Debug.Fail("Incorrect primitive type size!");
break;
}
}
}

private static unsafe void* GetSpanDataFrom(
RuntimeFieldHandle fldHandle,
RuntimeTypeHandle targetTypeHandle,
out int count);
out int count)
{
IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo();

if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0)
throw new ArgumentException(SR.Argument_BadFieldForInitializeArray);

TypeHandle th = new TypeHandle((void*)targetTypeHandle.Value);
Debug.Assert(!th.IsTypeDesc); // TypeDesc can't be used as generic parameter

if (!th.GetVerifierCorElementType().IsPrimitiveType()) // Enum is included
throw new ArgumentException(SR.Argument_MustBePrimitiveArray);

uint totalSize = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo));
uint targetTypeSize = th.AsMethodTable()->GetNumInstanceFieldBytes();

IntPtr data = RuntimeFieldHandle.GetStaticFieldAddress(fldInfo);
if (data % targetTypeSize != 0)
throw new ArgumentException(SR.Argument_BadFieldForInitializeArray);

if (!BitConverter.IsLittleEndian)
{
throw new PlatformNotSupportedException();
}

count = (int)(totalSize / targetTypeSize);
return (void*)data;
}

// GetObjectValue is intended to allow value classes to be manipulated as 'Object'
// but have aliasing behavior of a value class. The intent is that you would use
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1085,7 +1085,7 @@ public RuntimeFieldInfoStub(RuntimeFieldHandleInternal fieldHandle, object keepa
}

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

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern IntPtr GetStaticFieldAddress(RtFieldInfo field);
internal static extern IntPtr GetStaticFieldAddress(IRuntimeFieldInfo field);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeFieldHandle_GetFieldSize")]
internal static partial uint GetFieldSize(QCallFieldHandle field);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int GetToken(RtFieldInfo field);
Expand Down
113 changes: 0 additions & 113 deletions src/coreclr/classlibnative/bcltype/arraynative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,116 +441,3 @@ FCIMPL3(void, ArrayNative::SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE
}
}
FCIMPLEND

// This method will initialize an array from a TypeHandle to a field.

FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntimeFieldHandle structField)
{
FCALL_CONTRACT;

BASEARRAYREF arr = BASEARRAYREF(pArrayRef);
REFLECTFIELDREF refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField));
HELPER_METHOD_FRAME_BEGIN_2(arr, refField);

if ((arr == 0) || (refField == NULL))
COMPlusThrow(kArgumentNullException);

FieldDesc* pField = (FieldDesc*) refField->GetField();

if (!pField->IsRVA())
COMPlusThrow(kArgumentException);

// Note that we do not check that the field is actually in the PE file that is initializing
// the array. Basically the data being published is can be accessed by anyone with the proper
// permissions (C# marks these as assembly visibility, and thus are protected from outside
// snooping)

if (!CorTypeInfo::IsPrimitiveType(arr->GetArrayElementType()) && !arr->GetArrayElementTypeHandle().IsEnum())
COMPlusThrow(kArgumentException);

SIZE_T dwCompSize = arr->GetComponentSize();
SIZE_T dwElemCnt = arr->GetNumComponents();
SIZE_T dwTotalSize = dwCompSize * dwElemCnt;

DWORD size = pField->LoadSize();

// make certain you don't go off the end of the rva static
if (dwTotalSize > size)
COMPlusThrow(kArgumentException);

void *src = pField->GetStaticAddressHandle(NULL);
void *dest = arr->GetDataPtr();

#if BIGENDIAN
DWORD i;
switch (dwCompSize) {
case 1:
memcpyNoGCRefs(dest, src, dwElemCnt);
break;
case 2:
for (i = 0; i < dwElemCnt; i++)
*((UINT16*)dest + i) = GET_UNALIGNED_VAL16((UINT16*)src + i);
break;
case 4:
for (i = 0; i < dwElemCnt; i++)
*((UINT32*)dest + i) = GET_UNALIGNED_VAL32((UINT32*)src + i);
break;
case 8:
for (i = 0; i < dwElemCnt; i++)
*((UINT64*)dest + i) = GET_UNALIGNED_VAL64((UINT64*)src + i);
break;
default:
// should not reach here.
UNREACHABLE_MSG("Incorrect primitive type size!");
break;
}
#else
memcpyNoGCRefs(dest, src, dwTotalSize);
#endif

HELPER_METHOD_FRAME_END();
}
FCIMPLEND

FCIMPL3_VVI(void*, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count)
{
FCALL_CONTRACT;
struct
{
REFLECTFIELDREF refField;
REFLECTCLASSBASEREF refClass;
} gc;
gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField));
gc.refClass = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(FCALL_RTH_TO_REFLECTCLASS(targetTypeUnsafe));
void* data = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);

FieldDesc* pField = (FieldDesc*)gc.refField->GetField();

if (!pField->IsRVA())
COMPlusThrow(kArgumentException);

TypeHandle targetTypeHandle = gc.refClass->GetType();
if (!CorTypeInfo::IsPrimitiveType(targetTypeHandle.GetSignatureCorElementType()) && !targetTypeHandle.IsEnum())
COMPlusThrow(kArgumentException);

DWORD totalSize = pField->LoadSize();
DWORD targetTypeSize = targetTypeHandle.GetSize();

data = pField->GetStaticAddressHandle(NULL);
_ASSERTE(data != NULL);
_ASSERTE(count != NULL);

if (AlignUp((UINT_PTR)data, targetTypeSize) != (UINT_PTR)data)
COMPlusThrow(kArgumentException);

*count = (INT32)totalSize / targetTypeSize;

#if BIGENDIAN
COMPlusThrow(kPlatformNotSupportedException);
#endif

HELPER_METHOD_FRAME_END();
return data;
}
FCIMPLEND
7 changes: 0 additions & 7 deletions src/coreclr/classlibnative/bcltype/arraynative.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,6 @@ class ArrayNative
// This set of methods will set a value in an array
static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex);

// This method will initialize an array from a TypeHandle
// to a field.
static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField);

// This method will acquire data to create a span from a TypeHandle
// to a field.
static FCDECL3_VVI(void*, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count);
};

extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray);
Expand Down
2 changes: 0 additions & 2 deletions src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -439,8 +439,6 @@ FCFuncStart(gMonitorFuncs)
FCFuncEnd()

FCFuncStart(gRuntimeHelpers)
FCFuncElement("InitializeArray", ArrayNative::InitializeArray)
FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom)
FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate)
FCFuncElement("GetHashCode", ObjectNative::GetHashCode)
FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode)
Expand Down
18 changes: 18 additions & 0 deletions src/coreclr/vm/qcall.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,24 @@ class QCall
}
};

struct FieldHandle
{
Object ** m_ppObject;
FieldDesc * m_pField;

operator FieldDesc * ()
{
LIMITED_METHOD_CONTRACT;
return m_pField;
}

FieldDesc * operator -> ()
{
LIMITED_METHOD_CONTRACT;
return m_pField;
}
};

struct LoaderAllocatorHandle
{
LoaderAllocator * m_pLoaderAllocator;
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/qcallentrypoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ static const Entry s_QCall[] =
DllImportEntry(RuntimeModule_GetScopeName)
DllImportEntry(RuntimeModule_GetFullyQualifiedName)
DllImportEntry(RuntimeModule_GetTypes)
DllImportEntry(RuntimeFieldHandle_GetFieldSize)
DllImportEntry(StackFrame_GetMethodDescFromNativeIP)
DllImportEntry(ModuleBuilder_GetStringConstant)
DllImportEntry(ModuleBuilder_GetTypeRef)
Expand Down
27 changes: 21 additions & 6 deletions src/coreclr/vm/reflectioninvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1246,17 +1246,32 @@ FCIMPL1(void*, RuntimeFieldHandle::GetStaticFieldAddress, ReflectFieldObject *pF
// IsFastPathSupported needs to checked before calling this method.
_ASSERTE(IsFastPathSupportedHelper(pFieldDesc));

PTR_BYTE base = 0;
if (!pFieldDesc->IsRVA())
if (pFieldDesc->IsRVA())
{
// For RVA the base is ignored and offset is used.
base = pFieldDesc->GetBase();
Module* pModule = pFieldDesc->GetModule();
return pModule->GetRvaField(pFieldDesc->GetOffset());
}
else
{
PTR_BYTE base = pFieldDesc->GetBase();
return PTR_VOID(base + pFieldDesc->GetOffset());
}

return PTR_VOID(base + pFieldDesc->GetOffset());
}
FCIMPLEND

extern "C" UINT QCALLTYPE RuntimeFieldHandle_GetFieldSize(QCall::FieldHandle pField)
{
QCALL_CONTRACT;

UINT ret = 0;

BEGIN_QCALL;
ret = pField->LoadSize();
END_QCALL;

return ret;
}

extern "C" void QCALLTYPE ReflectionInvocation_CompileMethod(MethodDesc * pMD)
{
QCALL_CONTRACT;
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/runtimehandles.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ class RuntimeFieldHandle {
static FCDECL1(FC_BOOL_RET, AcquiresContextFromThis, FieldDesc *pField);
static FCDECL1(Object*, GetLoaderAllocator, FieldDesc *pField);
};
extern "C" UINT QCALLTYPE RuntimeFieldHandle_GetFieldSize(QCall::FieldHandle pField);

class ModuleHandle {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,25 @@ internal QCallTypeHandle(ref RuntimeTypeHandle rth)
_handle = rth.Value;
}
}

// Wraps IRuntimeFieldInfo into a handle. Used to pass IRuntimeFieldInfo to native code without letting it be collected
internal unsafe ref struct QCallFieldHandle
{
private void* _ptr;
private IntPtr _handle;

#if CORECLR
internal QCallFieldHandle(ref IRuntimeFieldInfo field)
{
_ptr = Unsafe.AsPointer(ref field);
_handle = field.Value.Value;
}
#endif

internal QCallFieldHandle(ref RuntimeFieldHandle rth)
{
_ptr = Unsafe.AsPointer(ref rth);
_handle = rth.Value;
}
}
}

0 comments on commit 0112e24

Please sign in to comment.