-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Convert some more array FCalls to managed #102739
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 23 commits
566b15e
8a592b9
0dce3d4
d29e5d8
5dd0e3c
527021c
e7d843a
2bb5fe7
f7da172
0924555
dc85381
f99c553
c5e6cc4
470b86f
c7821e6
5225203
07293e3
5da88c3
5222bca
335b2fb
93ec9d8
c3f5097
481e4d4
a266a30
b8e4584
5465a97
4ce4f51
30b7d8f
8a9c5bc
f95fa34
ebfd224
d9205c6
7eef724
8cfb58f
fbb9d71
324df76
089ae12
a5e7441
5552d63
133cd8a
e17bf0e
dd1e2dd
32b8789
582eaf1
1836ae5
cb6aa00
49cf866
7a0c398
db677ad
353715e
8fb1c96
26d4c80
5f7263f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| 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 | ||
huoyaoyuan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // 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); | ||
jkotas marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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( | ||
huoyaoyuan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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 | ||
huoyaoyuan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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; | ||
huoyaoyuan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // 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 | ||
|
|
@@ -652,6 +739,8 @@ public int MultiDimensionalArrayRank | |
| // Warning! UNLIKE the similarly named Reflection api, this method also returns "true" for Enums. | ||
| public bool IsPrimitive => (Flags & enum_flag_Category_Mask) is enum_flag_Category_PrimitiveValueType or enum_flag_Category_TruePrimitive; | ||
|
|
||
| public bool IsTruePrimitive => (Flags & enum_flag_Category_Mask) is enum_flag_Category_TruePrimitive; | ||
|
|
||
| public bool HasInstantiation => (Flags & enum_flag_HasComponentSize) == 0 && (Flags & enum_flag_GenericsMask) != enum_flag_GenericsMask_NonGeneric; | ||
|
|
||
| public bool IsGenericTypeDefinition => (Flags & (enum_flag_HasComponentSize | enum_flag_GenericsMask)) == enum_flag_GenericsMask_TypicalInst; | ||
|
|
@@ -681,6 +770,9 @@ public TypeHandle GetArrayElementTypeHandle() | |
|
|
||
| [MethodImpl(MethodImplOptions.InternalCall)] | ||
| public extern uint GetNumInstanceFieldBytes(); | ||
|
|
||
| [MethodImpl(MethodImplOptions.InternalCall)] | ||
| public extern CorElementType GetVerifierCorElementType(); | ||
| } | ||
|
|
||
| // Subset of src\vm\methodtable.h | ||
|
|
@@ -706,9 +798,9 @@ public bool CanCompareBitsOrUseFastGetHashCode | |
| } | ||
|
|
||
| /// <summary> | ||
| /// A type handle, which can wrap either a pointer to a <c>TypeDesc</c> or to a <see cref="MethodTable"/>. | ||
| /// A type handle, which can wrap either a pointer to a <see cref="TypeDesc"/> or to a <see cref="MethodTable"/>. | ||
| /// </summary> | ||
| internal unsafe struct TypeHandle | ||
| internal readonly unsafe partial struct TypeHandle | ||
| { | ||
| // Subset of src\vm\typehandle.h | ||
|
|
||
|
|
@@ -750,11 +842,67 @@ public bool IsTypeDesc | |
| return (MethodTable*)m_asTAddr; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets the <see cref="TypeDesc"/> pointer wrapped by the current instance. | ||
| /// </summary> | ||
| /// <remarks>This is only safe to call if <see cref="IsTypeDesc"/> returned <see langword="true"/>.</remarks> | ||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
| public TypeDesc* AsTypeDesc() | ||
| { | ||
| Debug.Assert(IsTypeDesc); | ||
|
|
||
| return (TypeDesc*)((nint)m_asTAddr - 2); | ||
| } | ||
|
|
||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
| public static TypeHandle TypeHandleOf<T>() | ||
| { | ||
| return new TypeHandle((void*)RuntimeTypeHandle.ToIntPtr(typeof(T).TypeHandle)); | ||
| } | ||
|
|
||
| public static bool AreSameType(TypeHandle left, TypeHandle right) => left.m_asTAddr == right.m_asTAddr; | ||
jkotas marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
| public bool CanCastTo(TypeHandle destTH) | ||
| { | ||
| CastResult result = CastCache.TryGet(CastHelpers.s_table!, (nuint)m_asTAddr, (nuint)destTH.m_asTAddr); | ||
|
|
||
| if (result != CastResult.MaybeCast) | ||
| return result == CastResult.CanCast; | ||
|
|
||
| return CanCastTo_NoCacheLookup(m_asTAddr, destTH.m_asTAddr); | ||
| } | ||
|
|
||
| [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeHandle_CanCastTo")] | ||
| [return: MarshalAs(UnmanagedType.Bool)] | ||
| private static partial bool CanCastTo_NoCacheLookup(void* fromTypeHnd, void* toTypeHnd); | ||
|
|
||
| public bool IsValueType | ||
| { | ||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
| get | ||
| { | ||
| return IsTypeDesc | ||
| ? AsTypeDesc()->GetInternalCorElementType() == CorElementType.ELEMENT_TYPE_VALUETYPE | ||
| : AsMethodTable()->IsValueType; | ||
| } | ||
| } | ||
|
|
||
| public bool IsInterface => !IsTypeDesc && AsMethodTable()->IsInterface; | ||
|
|
||
| public CorElementType GetVerifierCorElementType() => IsTypeDesc | ||
| ? AsTypeDesc()->GetInternalCorElementType() | ||
|
||
| : AsMethodTable()->GetVerifierCorElementType(); | ||
| } | ||
|
|
||
| internal unsafe struct TypeDesc | ||
| { | ||
| private readonly int m_typeAndFlags; | ||
|
|
||
| // This is the ELEMENT_TYPE* that would be used in the type sig for this type | ||
| // For enums this is the underlying type | ||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
| public CorElementType GetInternalCorElementType() => (CorElementType)(m_typeAndFlags & 0xFF); | ||
| } | ||
|
|
||
| // Helper structs used for tail calls via helper. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.