Skip to content

Commit

Permalink
Revert the known broken InternalSetValue
Browse files Browse the repository at this point in the history
  • Loading branch information
huoyaoyuan committed Jun 6, 2024
1 parent c3f5097 commit 481e4d4
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 94 deletions.
94 changes: 2 additions & 92 deletions src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -633,98 +633,8 @@ private unsafe nint GetFlattenedIndex(ReadOnlySpan<int> indices)
return result;
}

private unsafe void InternalSetValue(object? value, nint flattenedIndex)
{
MethodTable* pMethodTable = RuntimeHelpers.GetMethodTable(this);

TypeHandle arrayElementTypeHandle = pMethodTable->GetArrayElementTypeHandle();

// Legacy behavior (this handles pointers and function pointers)
if (arrayElementTypeHandle.IsTypeDesc)
{
ThrowHelper.ThrowNotSupportedException(ExceptionResource.Arg_TypeNotSupported);
}

Debug.Assert((nuint)flattenedIndex < NativeLength);

ref byte arrayDataRef = ref MemoryMarshal.GetArrayDataReference(this);

MethodTable* pElementMethodTable = arrayElementTypeHandle.AsMethodTable();

if (value == null)
{
// Null is the universal zero...
if (pElementMethodTable->IsValueType)
{
ref byte offsetDataRef = ref Unsafe.Add(ref arrayDataRef, flattenedIndex * pMethodTable->ComponentSize);
if (pElementMethodTable->ContainsGCPointers)
SpanHelpers.ClearWithReferences(ref Unsafe.As<byte, nint>(ref offsetDataRef), pElementMethodTable->GetNumInstanceFieldBytes());
else
SpanHelpers.ClearWithoutReferences(ref offsetDataRef, pElementMethodTable->GetNumInstanceFieldBytes());
}
else
{
ref object? elementRef = ref Unsafe.As<byte, object?>(ref arrayDataRef);
ref object? offsetElementRef = ref Unsafe.Add(ref elementRef, (nuint)flattenedIndex);
offsetElementRef = null;
}
}
else if (TypeHandle.AreSameType(arrayElementTypeHandle, TypeHandle.TypeHandleOf<object>()))
{
// Everything is compatible with Object
ref object? elementRef = ref Unsafe.As<byte, object?>(ref arrayDataRef);
ref object? offsetElementRef = ref Unsafe.Add(ref elementRef, (nuint)flattenedIndex);
offsetElementRef = value;
}
else if (!pElementMethodTable->IsValueType)
{
if (CastHelpers.IsInstanceOfAny(pElementMethodTable, value) == null)
throw new InvalidCastException(SR.InvalidCast_StoreArrayElement);

ref object? elementRef = ref Unsafe.As<byte, object?>(ref arrayDataRef);
ref object? offsetElementRef = ref Unsafe.Add(ref elementRef, (nuint)flattenedIndex);
offsetElementRef = value;
}
else
{
// value class or primitive type

ref byte offsetDataRef = ref Unsafe.Add(ref arrayDataRef, flattenedIndex * pMethodTable->ComponentSize);
if (CastHelpers.IsInstanceOfAny(pElementMethodTable, value) != null)
{
if (pElementMethodTable->IsNullable)
{
RuntimeHelpers.Unbox_Nullable(ref offsetDataRef, pElementMethodTable, value);
}
else if (pElementMethodTable->ContainsGCPointers)
{
Buffer.BulkMoveWithWriteBarrier(ref offsetDataRef, ref value.GetRawData(), pElementMethodTable->GetNumInstanceFieldBytes());
}
else
{
SpanHelpers.Memmove(ref offsetDataRef, ref value.GetRawData(), pElementMethodTable->GetNumInstanceFieldBytes());
}
}
else
{
// Allow enum -> primitive conversion, disallow primitive -> enum conversion
MethodTable* thSrc = RuntimeHelpers.GetMethodTable(value);
CorElementType srcType = thSrc->GetVerifierCorElementType();
CorElementType targetType = pElementMethodTable->GetVerifierCorElementType();

if (!srcType.IsPrimitiveType() || !targetType.IsPrimitiveType() || !pElementMethodTable->IsTruePrimitive)
throw new InvalidCastException(SR.InvalidCast_StoreArrayElement);

// Get a properly widened type
if (!RuntimeHelpers.CanPrimitiveWiden(srcType, targetType))
throw new ArgumentException(SR.Arg_PrimWiden);

PrimitiveWiden(ref value.GetRawData(), ref offsetDataRef, srcType, targetType);
}
}

GC.KeepAlive(this); // Keep the method table alive
}
[MethodImpl(MethodImplOptions.InternalCall)]
private extern void InternalSetValue(object? value, nint flattenedIndex);

public int Length => checked((int)Unsafe.As<RawArrayData>(this).Length);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ internal static unsafe class CastHelpers
// Unlike the IsInstanceOfInterface and IsInstanceOfClass functions,
// this test must deal with all kinds of type tests
[DebuggerHidden]
internal static object? IsInstanceOfAny(void* toTypeHnd, object? obj)
private static object? IsInstanceOfAny(void* toTypeHnd, object? obj)
{
if (obj != null)
{
Expand Down
87 changes: 87 additions & 0 deletions src/coreclr/classlibnative/bcltype/arraynative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,90 @@ void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT3
Done: ;
END_QCALL;
}

FCIMPL3(void, ArrayNative::SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex)
{
FCALL_CONTRACT;

BASEARRAYREF refThis(refThisUNSAFE);
OBJECTREF obj(objUNSAFE);

TypeHandle arrayElementType = refThis->GetArrayElementTypeHandle();

// Legacy behavior (this handles pointers and function pointers)
if (arrayElementType.IsTypeDesc())
{
FCThrowResVoid(kNotSupportedException, W("NotSupported_Type"));
}

_ASSERTE((SIZE_T)flattenedIndex < refThis->GetNumComponents());

MethodTable* pElementTypeMT = arrayElementType.GetMethodTable();
PREFIX_ASSUME(NULL != pElementTypeMT);

void* pData = refThis->GetDataPtr() + flattenedIndex * refThis->GetComponentSize();

if (obj == NULL)
{
// Null is the universal zero...
if (pElementTypeMT->IsValueType())
InitValueClass(pData,pElementTypeMT);
else
ClearObjectReference((OBJECTREF*)pData);
}
else
if (arrayElementType == TypeHandle(g_pObjectClass))
{
// Everything is compatible with Object
SetObjectReference((OBJECTREF*)pData,(OBJECTREF)obj);
}
else
if (!pElementTypeMT->IsValueType())
{
if (ObjIsInstanceOfCached(OBJECTREFToObject(obj), arrayElementType) != TypeHandle::CanCast)
{
HELPER_METHOD_FRAME_BEGIN_2(refThis, obj);

if (!ObjIsInstanceOf(OBJECTREFToObject(obj), arrayElementType))
COMPlusThrow(kInvalidCastException,W("InvalidCast_StoreArrayElement"));

HELPER_METHOD_FRAME_END();

// Refresh pData in case GC moved objects around
pData = refThis->GetDataPtr() + flattenedIndex * refThis->GetComponentSize();
}

SetObjectReference((OBJECTREF*)pData,obj);
}
else
{
// value class or primitive type

if (!pElementTypeMT->UnBoxInto(pData, obj))
{
HELPER_METHOD_FRAME_BEGIN_2(refThis, obj);

ARG_SLOT value = 0;

// Allow enum -> primitive conversion, disallow primitive -> enum conversion
TypeHandle thSrc = obj->GetTypeHandle();
CorElementType srcType = thSrc.GetVerifierCorElementType();
CorElementType targetType = arrayElementType.GetSignatureCorElementType();

if (!InvokeUtil::IsPrimitiveType(srcType) || !InvokeUtil::IsPrimitiveType(targetType))
COMPlusThrow(kInvalidCastException, W("InvalidCast_StoreArrayElement"));

// Get a properly widened type
InvokeUtil::CreatePrimitiveValue(targetType,srcType,obj,&value);

// Refresh pData in case GC moved objects around
pData = refThis->GetDataPtr() + flattenedIndex * refThis->GetComponentSize();

UINT cbSize = CorTypeInfo::Size(targetType);
memcpyNoGCRefs(pData, ArgSlotEndiannessFixup(&value, cbSize), cbSize);

HELPER_METHOD_FRAME_END();
}
}
}
FCIMPLEND
9 changes: 8 additions & 1 deletion src/coreclr/classlibnative/bcltype/arraynative.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@
#ifndef _ARRAYNATIVE_H_
#define _ARRAYNATIVE_H_

#include "qcall.h"
#include "fcall.h"

class ArrayNative
{
public:
// This set of methods will set a value in an array
static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex);
};

extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray);
extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHandle pArrayTypeHnd);
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,10 @@ FCFuncStart(gCastHelpers)
FCFuncElement("WriteBarrier", ::WriteBarrier_Helper)
FCFuncEnd()

FCFuncStart(gArrayFuncs)
FCFuncElement("InternalSetValue", ArrayNative::SetValue)
FCFuncEnd()

FCFuncStart(gBufferFuncs)
FCFuncElement("__BulkMoveWithWriteBarrier", Buffer::BulkMoveWithWriteBarrier)
FCFuncEnd()
Expand Down Expand Up @@ -531,6 +535,7 @@ FCFuncEnd()
// Note these have to remain sorted by name:namespace pair (Assert will wack you if you don't)
// The sorting is case-sensitive

FCClassElement("Array", "System", gArrayFuncs)
FCClassElement("AssemblyLoadContext", "System.Runtime.Loader", gAssemblyLoadContextFuncs)
FCClassElement("Buffer", "System", gBufferFuncs)
FCClassElement("CastHelpers", "System.Runtime.CompilerServices", gCastHelpers)
Expand Down

0 comments on commit 481e4d4

Please sign in to comment.