diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index 4684e0a9af4d43..44080e82e54977 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -251,6 +251,7 @@ + diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 62dd79dfb85ad9..4ba8ed5077ca54 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -150,8 +150,8 @@ public static unsafe void Copy(Array sourceArray, Array destinationArray, int le (uint)length <= (nuint)destinationArray.LongLength) { nuint byteCount = (uint)length * (nuint)pMT->ComponentSize; - ref byte src = ref sourceArray.GetRawSzArrayData(); - ref byte dst = ref destinationArray.GetRawSzArrayData(); + ref byte src = ref Unsafe.As(sourceArray).Data; + ref byte dst = ref Unsafe.As(destinationArray).Data; if (pMT->ContainsGCPointers) Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount); @@ -182,8 +182,8 @@ public static unsafe void Copy(Array sourceArray, int sourceIndex, Array destina { nuint elementSize = (nuint)pMT->ComponentSize; nuint byteCount = (uint)length * elementSize; - ref byte src = ref Unsafe.AddByteOffset(ref sourceArray.GetRawSzArrayData(), (uint)sourceIndex * elementSize); - ref byte dst = ref Unsafe.AddByteOffset(ref destinationArray.GetRawSzArrayData(), (uint)destinationIndex * elementSize); + ref byte src = ref Unsafe.AddByteOffset(ref Unsafe.As(sourceArray).Data, (uint)sourceIndex * elementSize); + ref byte dst = ref Unsafe.AddByteOffset(ref Unsafe.As(destinationArray).Data, (uint)destinationIndex * elementSize); if (pMT->ContainsGCPointers) Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount); diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 19cf8452c50a9a..368f1211455ed5 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -207,10 +207,6 @@ internal static unsafe nuint GetRawObjectDataSize(object obj) return rawSize; } - [Intrinsic] - internal static ref byte GetRawSzArrayData(this Array array) => - ref Unsafe.As(array).Data; - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static unsafe ref byte GetRawArrayData(this Array array) => // See comment on RawArrayData for details diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.CoreCLR.cs new file mode 100644 index 00000000000000..d21e4b2bd8e165 --- /dev/null +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.CoreCLR.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + public static partial class MemoryMarshal + { + /// + /// Returns a reference to the 0th element of . If the array is empty, returns a reference to where the 0th element + /// would have been stored. Such a reference may be used for pinning but must never be dereferenced. + /// + /// is . + /// + /// This method does not perform array variance checks. The caller must manually perform any array variance checks + /// if the caller wishes to write to the returned reference. + /// + [Intrinsic] + [NonVersionable] + public static ref T GetArrayDataReference(T[] array) => + ref Unsafe.As(ref Unsafe.As(array).Data); + } +} diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Text/Utf8Span.Conversion.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Text/Utf8Span.Conversion.cs index 5a692d95595662..48754b0097b3cb 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Text/Utf8Span.Conversion.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Text/Utf8Span.Conversion.cs @@ -5,7 +5,7 @@ using System.Buffers; using System.Diagnostics; using System.Globalization; -using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text.Unicode; namespace System.Text @@ -81,12 +81,12 @@ public unsafe char[] ToCharArray() Debug.Assert(pbUtf8Invalid == pbUtf8 + this.Length, "Invalid UTF-8 data seen in buffer."); char[] asUtf16 = new char[this.Length + utf16CodeUnitCountAdjustment]; - fixed (byte* pbUtf16 = &asUtf16.GetRawSzArrayData()) + fixed (char* pbUtf16 = &MemoryMarshal.GetArrayDataReference(asUtf16)) { - OperationStatus status = Utf8Utility.TranscodeToUtf16(pbUtf8, this.Length, (char*)pbUtf16, asUtf16.Length, out byte* pbUtf8End, out char* pchUtf16End); + OperationStatus status = Utf8Utility.TranscodeToUtf16(pbUtf8, this.Length, pbUtf16, asUtf16.Length, out byte* pbUtf8End, out char* pchUtf16End); Debug.Assert(status == OperationStatus.Done, "The buffer changed out from under us unexpectedly?"); Debug.Assert(pbUtf8End == pbUtf8 + this.Length, "The buffer changed out from under us unexpectedly?"); - Debug.Assert(pchUtf16End == ((char*)pbUtf16) + asUtf16.Length, "The buffer changed out from under us unexpectedly?"); + Debug.Assert(pchUtf16End == pbUtf16 + asUtf16.Length, "The buffer changed out from under us unexpectedly?"); return asUtf16; } diff --git a/src/coreclr/src/tools/Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs b/src/coreclr/src/tools/Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs new file mode 100644 index 00000000000000..dc4c6a7a197a99 --- /dev/null +++ b/src/coreclr/src/tools/Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs +{ + /// + /// Provides method bodies for System.Runtime.InteropServices.MemoryMarshal intrinsics. + /// + public static class MemoryMarshalIntrinsics + { + public static MethodIL EmitIL(MethodDesc method) + { + Debug.Assert(((MetadataType)method.OwningType).Name == "MemoryMarshal"); + string methodName = method.Name; + + if (methodName == "GetArrayDataReference") + { + ILEmitter emit = new ILEmitter(); + ILCodeStream codeStream = emit.NewCodeStream(); + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldflda, emit.NewToken(method.Context.SystemModule.GetKnownType("System.Runtime.CompilerServices", "RawArrayData").GetField("Data"))); + codeStream.Emit(ILOpcode.ret); + return emit.Link(method); + } + + // unknown method + return null; + } + } +} diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs index 44f6b791de4bea..311aed6f3d13b4 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs @@ -54,6 +54,11 @@ private MethodIL TryGetIntrinsicMethodIL(MethodDesc method) return UnsafeIntrinsics.EmitIL(method); } + if (mdType.Name == "MemoryMarshal" && mdType.Namespace == "System.Runtime.InteropServices") + { + return MemoryMarshalIntrinsics.EmitIL(method); + } + if (mdType.Name == "Volatile" && mdType.Namespace == "System.Threading") { return VolatileIntrinsics.EmitIL(method); diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 886f80a59e265f..e1efb7fbba9249 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -40,6 +40,7 @@ + diff --git a/src/coreclr/src/vm/jitinterface.cpp b/src/coreclr/src/vm/jitinterface.cpp index dd479a5bf375bd..507da32d65734b 100644 --- a/src/coreclr/src/vm/jitinterface.cpp +++ b/src/coreclr/src/vm/jitinterface.cpp @@ -7194,6 +7194,39 @@ bool getILIntrinsicImplementationForUnsafe(MethodDesc * ftn, return false; } +bool getILIntrinsicImplementationForMemoryMarshal(MethodDesc * ftn, + CORINFO_METHOD_INFO * methInfo) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(MscorlibBinder::IsClass(ftn->GetMethodTable(), CLASS__MEMORY_MARSHAL)); + + mdMethodDef tk = ftn->GetMemberDef(); + + if (tk == MscorlibBinder::GetMethod(METHOD__MEMORY_MARSHAL__GET_ARRAY_DATA_REFERENCE)->GetMemberDef()) + { + mdToken tokRawSzArrayData = MscorlibBinder::GetField(FIELD__RAW_ARRAY_DATA__DATA)->GetMemberDef(); + + static BYTE ilcode[] = { CEE_LDARG_0, + CEE_LDFLDA,0,0,0,0, + CEE_RET }; + + ilcode[2] = (BYTE)(tokRawSzArrayData); + ilcode[3] = (BYTE)(tokRawSzArrayData >> 8); + ilcode[4] = (BYTE)(tokRawSzArrayData >> 16); + ilcode[5] = (BYTE)(tokRawSzArrayData >> 24); + + methInfo->ILCode = const_cast(ilcode); + methInfo->ILCodeSize = sizeof(ilcode); + methInfo->maxStack = 1; + methInfo->EHcount = 0; + methInfo->options = (CorInfoOptions)0; + return true; + } + + return false; +} + bool getILIntrinsicImplementationForVolatile(MethodDesc * ftn, CORINFO_METHOD_INFO * methInfo) { @@ -7415,27 +7448,6 @@ bool getILIntrinsicImplementationForRuntimeHelpers(MethodDesc * ftn, return true; } - if (tk == MscorlibBinder::GetMethod(METHOD__RUNTIME_HELPERS__GET_RAW_SZ_ARRAY_DATA)->GetMemberDef()) - { - mdToken tokRawSzArrayData = MscorlibBinder::GetField(FIELD__RAW_ARRAY_DATA__DATA)->GetMemberDef(); - - static BYTE ilcode[] = { CEE_LDARG_0, - CEE_LDFLDA,0,0,0,0, - CEE_RET }; - - ilcode[2] = (BYTE)(tokRawSzArrayData); - ilcode[3] = (BYTE)(tokRawSzArrayData >> 8); - ilcode[4] = (BYTE)(tokRawSzArrayData >> 16); - ilcode[5] = (BYTE)(tokRawSzArrayData >> 24); - - methInfo->ILCode = const_cast(ilcode); - methInfo->ILCodeSize = sizeof(ilcode); - methInfo->maxStack = 1; - methInfo->EHcount = 0; - methInfo->options = (CorInfoOptions)0; - return true; - } - if (tk == MscorlibBinder::GetMethod(METHOD__RUNTIME_HELPERS__GET_METHOD_TABLE)->GetMemberDef()) { mdToken tokRawData = MscorlibBinder::GetField(FIELD__RAW_DATA__DATA)->GetMemberDef(); @@ -7639,6 +7651,10 @@ getMethodInfoHelper( { fILIntrinsic = getILIntrinsicImplementationForUnsafe(ftn, methInfo); } + else if (MscorlibBinder::IsClass(pMT, CLASS__MEMORY_MARSHAL)) + { + fILIntrinsic = getILIntrinsicImplementationForMemoryMarshal(ftn, methInfo); + } else if (MscorlibBinder::IsClass(pMT, CLASS__INTERLOCKED)) { fILIntrinsic = getILIntrinsicImplementationForInterlocked(ftn, methInfo); diff --git a/src/coreclr/src/vm/mscorlib.h b/src/coreclr/src/vm/mscorlib.h index bafcc278d1e40a..b9f3e45649b492 100644 --- a/src/coreclr/src/vm/mscorlib.h +++ b/src/coreclr/src/vm/mscorlib.h @@ -713,7 +713,6 @@ DEFINE_METHOD(RUNTIME_HELPERS, IS_REFERENCE_OR_CONTAINS_REFERENCES, IsRefer DEFINE_METHOD(RUNTIME_HELPERS, IS_BITWISE_EQUATABLE, IsBitwiseEquatable, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, GET_METHOD_TABLE, GetMethodTable, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, GET_RAW_DATA, GetRawData, NoSig) -DEFINE_METHOD(RUNTIME_HELPERS, GET_RAW_SZ_ARRAY_DATA, GetRawSzArrayData, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, GET_RAW_ARRAY_DATA, GetRawArrayData, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, GET_UNINITIALIZED_OBJECT, GetUninitializedObject, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, ENUM_EQUALS, EnumEquals, NoSig) @@ -743,6 +742,9 @@ DEFINE_METHOD(UNSAFE, PTR_READ_UNALIGNED, ReadUnaligned, GM_Pt DEFINE_METHOD(UNSAFE, PTR_WRITE_UNALIGNED, WriteUnaligned, GM_PtrVoid_T_RetVoid) DEFINE_METHOD(UNSAFE, SKIPINIT, SkipInit, GM_RefT_RetVoid) +DEFINE_CLASS(MEMORY_MARSHAL, Interop, MemoryMarshal) +DEFINE_METHOD(MEMORY_MARSHAL, GET_ARRAY_DATA_REFERENCE, GetArrayDataReference, NoSig) + DEFINE_CLASS(INTERLOCKED, Threading, Interlocked) DEFINE_METHOD(INTERLOCKED, COMPARE_EXCHANGE_T, CompareExchange, GM_RefT_T_T_RetT) DEFINE_METHOD(INTERLOCKED, COMPARE_EXCHANGE_OBJECT,CompareExchange, SM_RefObject_Object_Object_RetObject) diff --git a/src/libraries/System.Memory/ref/System.Memory.cs b/src/libraries/System.Memory/ref/System.Memory.cs index e3056f06405e19..bc17f4d0780fc9 100644 --- a/src/libraries/System.Memory/ref/System.Memory.cs +++ b/src/libraries/System.Memory/ref/System.Memory.cs @@ -473,6 +473,7 @@ public static partial class MemoryMarshal public static System.Memory CreateFromPinnedArray(T[]? array, int start, int length) { throw null; } public static System.ReadOnlySpan CreateReadOnlySpan(ref T reference, int length) { throw null; } public static System.Span CreateSpan(ref T reference, int length) { throw null; } + public static ref T GetArrayDataReference(T[] array) { throw null; } public static ref T GetReference(System.ReadOnlySpan span) { throw null; } public static ref T GetReference(System.Span span) { throw null; } public static T Read(System.ReadOnlySpan source) where T : struct { throw null; } diff --git a/src/libraries/System.Memory/tests/MemoryMarshal/GetArrayDataReference.cs b/src/libraries/System.Memory/tests/MemoryMarshal/GetArrayDataReference.cs new file mode 100644 index 00000000000000..64a1282db580b8 --- /dev/null +++ b/src/libraries/System.Memory/tests/MemoryMarshal/GetArrayDataReference.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.SpanTests +{ + public static partial class MemoryMarshalTests + { + [Fact] + public static void GetArrayDataReference_NullInput_ThrowsNullRef() + { + Assert.Throws(() => MemoryMarshal.GetArrayDataReference((object[])null)); + } + + [Fact] + public static void GetArrayDataReference_NonEmptyInput_ReturnsRefToFirstElement() + { + int[] theArray = new int[] { 10, 20, 30 }; + Assert.True(Unsafe.AreSame(ref theArray[0], ref MemoryMarshal.GetArrayDataReference(theArray))); + } + + [Fact] + public static unsafe void GetArrayDataReference_EmptyInput_ReturnsRefToWhereFirstElementWouldBe() + { + int[] theArray = new int[0]; + + ref int theRef = ref MemoryMarshal.GetArrayDataReference(theArray); + + Assert.True(Unsafe.AsPointer(ref theRef) != null); + Assert.True(Unsafe.AreSame(ref theRef, ref MemoryMarshal.GetReference(theArray.AsSpan()))); + } + + [Fact] + public static void GetArrayDataReference_IgnoresArrayVarianceChecks() + { + string[] strArr = new string[] { "Hello" }; + + // 'ref object' instead of 'ref string' because GetArrayDataReference skips array variance checks. + // We can deref it but we must not write to it unless we know the value being written is also a string. + ref object refObj = ref MemoryMarshal.GetArrayDataReference(strArr); + + Assert.True(Unsafe.AreSame(ref refObj, ref Unsafe.As(ref strArr[0]))); + } + } +} diff --git a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj index b094bda62c68a5..842e212e0b5fca 100644 --- a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj +++ b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj @@ -200,6 +200,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.cs b/src/libraries/System.Private.CoreLib/src/System/Array.cs index f280123b395bfe..ed9fe71a6f76c1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.cs @@ -9,6 +9,7 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Internal.Runtime.CompilerServices; namespace System @@ -1115,7 +1116,7 @@ public static int IndexOf(T[] array, T value, int startIndex, int count) if (Unsafe.SizeOf() == sizeof(byte)) { int result = SpanHelpers.IndexOf( - ref Unsafe.Add(ref array.GetRawSzArrayData(), startIndex), + ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(array)), startIndex), Unsafe.As(ref value), count); return (result >= 0 ? startIndex : 0) + result; @@ -1123,7 +1124,7 @@ ref Unsafe.Add(ref array.GetRawSzArrayData(), startIndex), else if (Unsafe.SizeOf() == sizeof(char)) { int result = SpanHelpers.IndexOf( - ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), startIndex), + ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(array)), startIndex), Unsafe.As(ref value), count); return (result >= 0 ? startIndex : 0) + result; @@ -1131,7 +1132,7 @@ ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), startIn else if (Unsafe.SizeOf() == sizeof(int)) { int result = SpanHelpers.IndexOf( - ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), startIndex), + ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(array)), startIndex), Unsafe.As(ref value), count); return (result >= 0 ? startIndex : 0) + result; @@ -1139,7 +1140,7 @@ ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), startInd else if (Unsafe.SizeOf() == sizeof(long)) { int result = SpanHelpers.IndexOf( - ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), startIndex), + ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(array)), startIndex), Unsafe.As(ref value), count); return (result >= 0 ? startIndex : 0) + result; @@ -1363,7 +1364,7 @@ public static int LastIndexOf(T[] array, T value, int startIndex, int count) { int endIndex = startIndex - count + 1; int result = SpanHelpers.LastIndexOf( - ref Unsafe.Add(ref array.GetRawSzArrayData(), endIndex), + ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(array)), endIndex), Unsafe.As(ref value), count); @@ -1373,7 +1374,7 @@ ref Unsafe.Add(ref array.GetRawSzArrayData(), endIndex), { int endIndex = startIndex - count + 1; int result = SpanHelpers.LastIndexOf( - ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), endIndex), + ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(array)), endIndex), Unsafe.As(ref value), count); @@ -1383,7 +1384,7 @@ ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), endInde { int endIndex = startIndex - count + 1; int result = SpanHelpers.LastIndexOf( - ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), endIndex), + ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(array)), endIndex), Unsafe.As(ref value), count); @@ -1393,7 +1394,7 @@ ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), endIndex { int endIndex = startIndex - count + 1; int result = SpanHelpers.LastIndexOf( - ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), endIndex), + ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(array)), endIndex), Unsafe.As(ref value), count); @@ -1515,7 +1516,7 @@ public static void Reverse(T[] array, int index, int length) if (length <= 1) return; - ref T first = ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), index); + ref T first = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), index); ref T last = ref Unsafe.Add(ref Unsafe.Add(ref first, length), -1); do { @@ -1721,7 +1722,7 @@ public static void Sort(T[] array) if (array.Length > 1) { - var span = new Span(ref Unsafe.As(ref array.GetRawSzArrayData()), array.Length); + var span = new Span(ref MemoryMarshal.GetArrayDataReference(array), array.Length); ArraySortHelper.Default.Sort(span, null); } } @@ -1770,7 +1771,7 @@ public static void Sort(T[] array, int index, int length, System.Collections. if (length > 1) { - var span = new Span(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), index), length); + var span = new Span(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), index), length); ArraySortHelper.Default.Sort(span, comparer); } } @@ -1794,8 +1795,8 @@ public static void Sort(TKey[] keys, TValue[]? items, int index, i return; } - var spanKeys = new Span(ref Unsafe.Add(ref Unsafe.As(ref keys.GetRawSzArrayData()), index), length); - var spanItems = new Span(ref Unsafe.Add(ref Unsafe.As(ref items.GetRawSzArrayData()), index), length); + var spanKeys = new Span(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(keys), index), length); + var spanItems = new Span(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(items), index), length); ArraySortHelper.Default.Sort(spanKeys, spanItems, comparer); } } @@ -1812,7 +1813,7 @@ public static void Sort(T[] array, Comparison comparison) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison); } - var span = new Span(ref Unsafe.As(ref array.GetRawSzArrayData()), array.Length); + var span = new Span(ref MemoryMarshal.GetArrayDataReference(array), array.Length); ArraySortHelper.Sort(span, comparison); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Memory.cs b/src/libraries/System.Private.CoreLib/src/System/Memory.cs index bc437644c1f989..49e37a0818b952 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Memory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Memory.cs @@ -321,13 +321,17 @@ public unsafe Span Span { // We know the object is not null, it's not a string, and it is variable-length. The only // remaining option is for it to be a T[] (or a U[] which is blittable to T[], like int[] - // and uint[]). Otherwise somebody used private reflection to set this field, and we're not - // too worried about type safety violations at this point. + // and uint[]). As a special case of this, ROM allows some amount of array variance + // that Memory disallows. For example, an array of actual type string[] cannot be turned + // into a Memory or a Span, but it can be turned into a ROM/ROS. + // We'll assume these checks succeeded because they're performed during Memory construction. + // It's always possible for somebody to use private reflection to bypass these checks, but + // preventing type safety violations due to misuse of reflection is out of scope of this logic. // 'tmpObject is T[]' below also handles things like int[] <-> uint[] being convertible Debug.Assert(tmpObject is T[]); - refToReturn = ref Unsafe.As(ref Unsafe.As(tmpObject).GetRawSzArrayData()); + refToReturn = ref MemoryMarshal.GetArrayDataReference(Unsafe.As(tmpObject)); lengthOfUnderlyingSpan = Unsafe.As(tmpObject).Length; } else @@ -443,13 +447,13 @@ public unsafe MemoryHandle Pin() // Array is already pre-pinned if (_index < 0) { - void* pointer = Unsafe.Add(Unsafe.AsPointer(ref Unsafe.As(tmpObject).GetRawSzArrayData()), _index & ReadOnlyMemory.RemoveFlagsBitMask); + void* pointer = Unsafe.Add(Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(tmpObject))), _index & ReadOnlyMemory.RemoveFlagsBitMask); return new MemoryHandle(pointer); } else { GCHandle handle = GCHandle.Alloc(tmpObject, GCHandleType.Pinned); - void* pointer = Unsafe.Add(Unsafe.AsPointer(ref Unsafe.As(tmpObject).GetRawSzArrayData()), _index); + void* pointer = Unsafe.Add(Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(tmpObject))), _index); return new MemoryHandle(pointer, handle); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index f3e86eb363575b..63534adc023104 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -39,7 +39,7 @@ public static Span AsSpan(this T[]? array, int start) if ((uint)start > (uint)array.Length) ThrowHelper.ThrowArgumentOutOfRangeException(); - return new Span(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), start), array.Length - start); + return new Span(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), start), array.Length - start); } /// @@ -63,7 +63,7 @@ public static Span AsSpan(this T[]? array, Index startIndex) if ((uint)actualIndex > (uint)array.Length) ThrowHelper.ThrowArgumentOutOfRangeException(); - return new Span(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), actualIndex), array.Length - actualIndex); + return new Span(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), actualIndex), array.Length - actualIndex); } /// @@ -87,7 +87,7 @@ public static Span AsSpan(this T[]? array, Range range) ThrowHelper.ThrowArrayTypeMismatchException(); (int start, int length) = range.GetOffsetAndLength(array.Length); - return new Span(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), start), length); + return new Span(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), start), length); } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs b/src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs index 2510e0fe738729..e01cf29325dd8b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs @@ -243,13 +243,17 @@ public unsafe ReadOnlySpan Span { // We know the object is not null, it's not a string, and it is variable-length. The only // remaining option is for it to be a T[] (or a U[] which is blittable to T[], like int[] - // and uint[]). Otherwise somebody used private reflection to set this field, and we're not - // too worried about type safety violations at this point. + // and uint[]). As a special case of this, ROM allows some amount of array variance + // that Memory disallows. For example, an array of actual type string[] cannot be turned + // into a Memory or a Span, but it can be turned into a ROM/ROS. + // We'll assume these checks succeeded because they're performed during Memory construction. + // It's always possible for somebody to use private reflection to bypass these checks, but + // preventing type safety violations due to misuse of reflection is out of scope of this logic. // 'tmpObject is T[]' below also handles things like int[] <-> uint[] being convertible Debug.Assert(tmpObject is T[]); - refToReturn = ref Unsafe.As(ref Unsafe.As(tmpObject).GetRawSzArrayData()); + refToReturn = ref MemoryMarshal.GetArrayDataReference(Unsafe.As(tmpObject)); lengthOfUnderlyingSpan = Unsafe.As(tmpObject).Length; } else @@ -358,13 +362,13 @@ public unsafe MemoryHandle Pin() // Array is already pre-pinned if (_index < 0) { - void* pointer = Unsafe.Add(Unsafe.AsPointer(ref Unsafe.As(tmpObject).GetRawSzArrayData()), _index & RemoveFlagsBitMask); + void* pointer = Unsafe.Add(Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(tmpObject))), _index & RemoveFlagsBitMask); return new MemoryHandle(pointer); } else { GCHandle handle = GCHandle.Alloc(tmpObject, GCHandleType.Pinned); - void* pointer = Unsafe.Add(Unsafe.AsPointer(ref Unsafe.As(tmpObject).GetRawSzArrayData()), _index); + void* pointer = Unsafe.Add(Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(tmpObject))), _index); return new MemoryHandle(pointer, handle); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs b/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs index 94cc4d2462a6c5..80f2e76d71df96 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; @@ -49,7 +50,7 @@ public ReadOnlySpan(T[]? array) return; // returns default } - _pointer = new ByReference(ref Unsafe.As(ref array.GetRawSzArrayData())); + _pointer = new ByReference(ref MemoryMarshal.GetArrayDataReference(array)); _length = array.Length; } @@ -83,7 +84,7 @@ public ReadOnlySpan(T[]? array, int start, int length) ThrowHelper.ThrowArgumentOutOfRangeException(); #endif - _pointer = new ByReference(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), start)); + _pointer = new ByReference(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), start)); _length = length; } @@ -386,7 +387,7 @@ public T[] ToArray() return Array.Empty(); var destination = new T[_length]; - Buffer.Memmove(ref Unsafe.As(ref destination.GetRawSzArrayData()), ref _pointer.Value, (nuint)_length); + Buffer.Memmove(ref MemoryMarshal.GetArrayDataReference(destination), ref _pointer.Value, (nuint)_length); return destination; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index cd2971f0350b2d..e094e4565b9094 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Reflection; using Internal.Runtime.CompilerServices; @@ -37,8 +38,8 @@ public static T[] GetSubArray(T[] array, Range range) var dest = new T[length]; Buffer.Memmove( - ref Unsafe.As(ref dest.GetRawSzArrayData()), - ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), offset), + ref MemoryMarshal.GetArrayDataReference(dest), + ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), offset), (uint)length); return dest; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs index 4eaef53ec42c62..8422e76892c87e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs @@ -170,7 +170,7 @@ public static unsafe IntPtr UnsafeAddrOfPinnedArrayElement(T[] arr, int index if (arr is null) throw new ArgumentNullException(nameof(arr)); - void* pRawData = Unsafe.AsPointer(ref arr.GetRawSzArrayData()); + void* pRawData = Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(arr)); return (IntPtr)((byte*)pRawData + (uint)index * (nuint)Unsafe.SizeOf()); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs index a61e0220885af3..167ca08f0c275c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs @@ -3,12 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Buffers; -using System.Runtime.CompilerServices; using System.Collections.Generic; using System.Diagnostics; - -using Internal.Runtime.CompilerServices; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; namespace System.Runtime.InteropServices { @@ -16,7 +15,7 @@ namespace System.Runtime.InteropServices /// Provides a collection of methods for interoperating with , , /// , and . /// - public static class MemoryMarshal + public static partial class MemoryMarshal { /// /// Casts a Span of one primitive type to Span of bytes. diff --git a/src/libraries/System.Private.CoreLib/src/System/Span.cs b/src/libraries/System.Private.CoreLib/src/System/Span.cs index 149107b76900f5..ff8ed41c7b8fc2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Span.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Span.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; @@ -52,7 +53,7 @@ public Span(T[]? array) if (default(T)! == null && array.GetType() != typeof(T[])) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757) ThrowHelper.ThrowArrayTypeMismatchException(); - _pointer = new ByReference(ref Unsafe.As(ref array.GetRawSzArrayData())); + _pointer = new ByReference(ref MemoryMarshal.GetArrayDataReference(array)); _length = array.Length; } @@ -89,7 +90,7 @@ public Span(T[]? array, int start, int length) ThrowHelper.ThrowArgumentOutOfRangeException(); #endif - _pointer = new ByReference(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), start)); + _pointer = new ByReference(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), start)); _length = length; } @@ -472,7 +473,7 @@ public T[] ToArray() return Array.Empty(); var destination = new T[_length]; - Buffer.Memmove(ref Unsafe.As(ref destination.GetRawSzArrayData()), ref _pointer.Value, (nuint)_length); + Buffer.Memmove(ref MemoryMarshal.GetArrayDataReference(destination), ref _pointer.Value, (nuint)_length); return destination; } }