Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
===========================================================*/

using System.Diagnostics;
using System.Runtime;
using System.Runtime.InteropServices;

using CorElementType = System.Reflection.CorElementType;
Expand Down Expand Up @@ -85,13 +84,5 @@ private EETypeElementType ElementType
return _value->ElementType;
}
}

internal RuntimeImports.RhCorElementTypeInfo CorElementTypeInfo
{
get
{
return RuntimeImports.GetRhCorElementTypeInfo(CorElementType);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Runtime.CompilerServices;

using MethodTable = Internal.Runtime.MethodTable;
using EETypeElementType = Internal.Runtime.EETypeElementType;

namespace System
{
Expand Down Expand Up @@ -126,65 +127,65 @@ private static Exception ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(obje
return CreateChangeTypeException(srcEEType, dstEEType, semantics);
}

CorElementType dstCorElementType = new EETypePtr(dstEEType).CorElementType;
if (!new EETypePtr(srcEEType).CorElementTypeInfo.CanWidenTo(dstCorElementType))
EETypeElementType dstElementType = dstEEType->ElementType;
if (!CanPrimitiveWiden(dstElementType, srcEEType->ElementType))
{
dstObject = null;
return CreateChangeTypeArgumentException(srcEEType, dstEEType);
}

switch (dstCorElementType)
switch (dstElementType)
{
case CorElementType.ELEMENT_TYPE_BOOLEAN:
case EETypeElementType.Boolean:
dstObject = Convert.ToBoolean(srcObject);
break;

case CorElementType.ELEMENT_TYPE_CHAR:
case EETypeElementType.Char:
char charValue = Convert.ToChar(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, charValue) : charValue;
break;

case CorElementType.ELEMENT_TYPE_I1:
case EETypeElementType.SByte:
sbyte sbyteValue = Convert.ToSByte(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, sbyteValue) : sbyteValue;
break;

case CorElementType.ELEMENT_TYPE_I2:
case EETypeElementType.Int16:
short shortValue = Convert.ToInt16(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, shortValue) : shortValue;
break;

case CorElementType.ELEMENT_TYPE_I4:
case EETypeElementType.Int32:
int intValue = Convert.ToInt32(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, intValue) : intValue;
break;

case CorElementType.ELEMENT_TYPE_I8:
case EETypeElementType.Int64:
long longValue = Convert.ToInt64(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, longValue) : longValue;
break;

case CorElementType.ELEMENT_TYPE_U1:
case EETypeElementType.Byte:
byte byteValue = Convert.ToByte(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, byteValue) : byteValue;
break;

case CorElementType.ELEMENT_TYPE_U2:
case EETypeElementType.UInt16:
ushort ushortValue = Convert.ToUInt16(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, ushortValue) : ushortValue;
break;

case CorElementType.ELEMENT_TYPE_U4:
case EETypeElementType.UInt32:
uint uintValue = Convert.ToUInt32(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, uintValue) : uintValue;
break;

case CorElementType.ELEMENT_TYPE_U8:
case EETypeElementType.UInt64:
ulong ulongValue = Convert.ToUInt64(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, (long)ulongValue) : ulongValue;
break;

case CorElementType.ELEMENT_TYPE_R4:
case EETypeElementType.Single:
if (new EETypePtr(srcEEType).CorElementType == CorElementType.ELEMENT_TYPE_CHAR)
{
dstObject = (float)(char)srcObject;
Expand All @@ -195,7 +196,7 @@ private static Exception ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(obje
}
break;

case CorElementType.ELEMENT_TYPE_R8:
case EETypeElementType.Double:
if (new EETypePtr(srcEEType).CorElementType == CorElementType.ELEMENT_TYPE_CHAR)
{
dstObject = (double)(char)srcObject;
Expand All @@ -207,7 +208,7 @@ private static Exception ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(obje
break;

default:
Debug.Fail("Unexpected CorElementType: " + dstCorElementType + ": Not a valid widening target.");
Debug.Fail("Unexpected CorElementType: " + dstElementType + ": Not a valid widening target.");
dstObject = null;
return CreateChangeTypeException(srcEEType, dstEEType, semantics);
}
Expand All @@ -216,6 +217,34 @@ private static Exception ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(obje
return null;
}

private static bool CanPrimitiveWiden(EETypeElementType destType, EETypeElementType srcType)
{
Debug.Assert(destType is < EETypeElementType.ValueType and >= EETypeElementType.Boolean);
Debug.Assert(srcType is < EETypeElementType.ValueType and >= EETypeElementType.Boolean);

ReadOnlySpan<ushort> primitiveAttributes = [
0x0000, // Unknown
0x0000, // Void
0x0004, // Boolean (W = BOOL)
0xCf88, // Char (W = U2, CHAR, I4, U4, I8, U8, R4, R8)
0xC550, // SByte (W = I1, I2, I4, I8, R4, R8)
0xCFE8, // Byte (W = CHAR, U1, I2, U2, I4, U4, I8, U8, R4, R8)
0xC540, // Int16 (W = I2, I4, I8, R4, R8)
0xCF88, // UInt16 (W = U2, CHAR, I4, U4, I8, U8, R4, R8)
0xC500, // Int32 (W = I4, I8, R4, R8)
0xCE00, // UInt32 (W = U4, I8, R4, R8)
0xC400, // Int64 (W = I8, R4, R8)
0xC800, // UInt64 (W = U8, R4, R8)
0x0000, // IntPtr
0x0000, // UIntPtr
0xC000, // Single (W = R4, R8)
0x8000, // Double (W = R8)
];

ushort mask = (ushort)(1 << (byte)destType);
return (primitiveAttributes[(int)srcType & 0xF] & mask) != 0;
}

private static Exception ConvertPointerIfPossible(object srcObject, MethodTable* dstEEType, CheckArgumentSemantics semantics, out object dstPtr)
{
if (srcObject is IntPtr or UIntPtr)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1106,119 +1106,5 @@ internal static partial void NativeRuntimeEventSource_LogWaitHandleWaitStart(
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
internal static unsafe partial void RhCpuIdEx(int* cpuInfo, int functionId, int subFunctionId);
#endif

internal static RhCorElementTypeInfo GetRhCorElementTypeInfo(CorElementType elementType)
{
return RhCorElementTypeInfo.GetRhCorElementTypeInfo(elementType);
}

internal struct RhCorElementTypeInfo
{
public bool IsPrimitive
{
get
{
return 0 != (_flags & RhCorElementTypeInfoFlags.IsPrimitive);
}
}

//
// This is a port of InvokeUtil::CanPrimitiveWiden() in the desktop runtime. This is used by various apis such as Array.SetValue()
// and Delegate.DynamicInvoke() which allow value-preserving widenings from one primitive type to another.
//
public bool CanWidenTo(CorElementType targetElementType)
{
// Caller expected to ensure that both sides are primitive before calling us.
Debug.Assert(this.IsPrimitive);
Debug.Assert(GetRhCorElementTypeInfo(targetElementType).IsPrimitive);

// Once we've asserted that the target is a primitive, we can also assert that it is >= ET_BOOLEAN.
Debug.Assert(targetElementType >= CorElementType.ELEMENT_TYPE_BOOLEAN);
byte targetElementTypeAsByte = (byte)targetElementType;
ushort mask = (ushort)(1 << targetElementTypeAsByte); // This is expected to overflow on larger ET_I and ET_U - this is ok and anticipated.
if (0 != (_widenMask & mask))
return true;
return false;
}

internal static RhCorElementTypeInfo GetRhCorElementTypeInfo(CorElementType elementType)
{
// The _lookupTable array only covers a subset of RhCorElementTypes, so we return a default
// info when someone asks for an elementType which does not have an entry in the table.
if ((int)elementType > s_lookupTable.Length)
return default(RhCorElementTypeInfo);

return s_lookupTable[(int)elementType];
}


private RhCorElementTypeInfoFlags _flags;

[Flags]
private enum RhCorElementTypeInfoFlags : byte
{
IsValid = 0x01, // Set for all valid CorElementTypeInfo's
IsPrimitive = 0x02, // Is it a primitive type (as defined by TypeInfo.IsPrimitive)
IsFloat = 0x04, // Is it a floating point type
}

private ushort _widenMask;

private static RhCorElementTypeInfo[] s_lookupTable = new RhCorElementTypeInfo[]
{
// index = 0x0
new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 },
// index = 0x1
new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 },
// index = 0x2 = ELEMENT_TYPE_BOOLEAN (W = BOOL)
new RhCorElementTypeInfo { _widenMask = 0x0004, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive },
// index = 0x3 = ELEMENT_TYPE_CHAR (W = U2, CHAR, I4, U4, I8, U8, R4, R8) (U2 == Char)
new RhCorElementTypeInfo { _widenMask = 0x3f88, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive },
// index = 0x4 = ELEMENT_TYPE_I1 (W = I1, I2, I4, I8, R4, R8)
new RhCorElementTypeInfo { _widenMask = 0x3550, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive },
// index = 0x5 = ELEMENT_TYPE_U1 (W = CHAR, U1, I2, U2, I4, U4, I8, U8, R4, R8)
new RhCorElementTypeInfo { _widenMask = 0x3FE8, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive },
// index = 0x6 = ELEMENT_TYPE_I2 (W = I2, I4, I8, R4, R8)
new RhCorElementTypeInfo { _widenMask = 0x3540, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive },
// index = 0x7 = ELEMENT_TYPE_U2 (W = U2, CHAR, I4, U4, I8, U8, R4, R8)
new RhCorElementTypeInfo { _widenMask = 0x3F88, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive },
// index = 0x8 = ELEMENT_TYPE_I4 (W = I4, I8, R4, R8)
new RhCorElementTypeInfo { _widenMask = 0x3500, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive },
// index = 0x9 = ELEMENT_TYPE_U4 (W = U4, I8, R4, R8)
new RhCorElementTypeInfo { _widenMask = 0x3E00, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive },
// index = 0xa = ELEMENT_TYPE_I8 (W = I8, R4, R8)
new RhCorElementTypeInfo { _widenMask = 0x3400, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive },
// index = 0xb = ELEMENT_TYPE_U8 (W = U8, R4, R8)
new RhCorElementTypeInfo { _widenMask = 0x3800, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive },
// index = 0xc = ELEMENT_TYPE_R4 (W = R4, R8)
new RhCorElementTypeInfo { _widenMask = 0x3000, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive|RhCorElementTypeInfoFlags.IsFloat },
// index = 0xd = ELEMENT_TYPE_R8 (W = R8)
new RhCorElementTypeInfo { _widenMask = 0x2000, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive|RhCorElementTypeInfoFlags.IsFloat },
// index = 0xe
new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 },
// index = 0xf
new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 },
// index = 0x10
new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 },
// index = 0x11
new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 },
// index = 0x12
new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 },
// index = 0x13
new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 },
// index = 0x14
new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 },
// index = 0x15
new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 },
// index = 0x16
new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 },
// index = 0x17
new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 },
// index = 0x18 = ELEMENT_TYPE_I
new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive },
// index = 0x19 = ELEMENT_TYPE_U
new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive },
};
}
}
}