MembersMustExist : Member 'System.String System.Runtime.CompilerServices.RuntimeFeature.DefaultImplementationsOfInterfaces' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Runtime.CompilerServices.RuntimeHelpers.PrepareMethod(System.RuntimeMethodHandle)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Runtime.CompilerServices.RuntimeHelpers.PrepareMethod(System.RuntimeMethodHandle, System.RuntimeTypeHandle[])' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.String.GetPinnableReference()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Text.Rune.DecodeFromUtf16(System.ReadOnlySpan<System.Char>, System.Text.Rune, System.Int32)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Text.Rune.DecodeLastFromUtf16(System.ReadOnlySpan<System.Char>, System.Text.Rune, System.Int32)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Text.Rune.DecodeFromUtf8(System.ReadOnlySpan<System.Byte>, System.Text.Rune, System.Int32)' does not exist in the implementation but it does exist in the contract.
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using System.Reflection.Emit;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.DotNet.RemoteExecutor;
}
[Theory]
+ [InlineData("")] // empty string
+ [InlineData("hello")] // non-empty string
+ public unsafe static void GetPinnableReference_ReturnsSameAsGCHandleAndLegacyFixed(string input)
+ {
+ Assert.NotNull(input); // test shouldn't have null input
+
+ // First, ensure the value pointed to by GetPinnableReference is correct.
+ // It should point to the first character (or the null terminator for empty inputs).
+
+ ref readonly char rChar = ref input.GetPinnableReference();
+ Assert.Equal((input.Length > 0) ? input[0] : '\0', rChar);
+
+ // Next, ensure that GetPinnableReference() and GCHandle.AddrOfPinnedObject agree
+ // on the address being returned.
+
+ GCHandle gcHandle = GCHandle.Alloc(input, GCHandleType.Pinned);
+ try
+ {
+ Assert.Equal((IntPtr)Unsafe.AsPointer(ref Unsafe.AsRef(in rChar)), gcHandle.AddrOfPinnedObject());
+ }
+ finally
+ {
+ gcHandle.Free();
+ }
+
+ // Next, ensure that GetPinnableReference matches the string projected as a ROS<char>.
+
+ Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(in rChar), ref MemoryMarshal.GetReference((ReadOnlySpan<char>)input)));
+
+ // Finally, ensure that GetPinnableReference matches the legacy 'fixed' keyword.
+
+ DynamicMethod dynamicMethod = new DynamicMethod("tester", typeof(bool), new[] { typeof(string) });
+ ILGenerator ilGen = dynamicMethod.GetILGenerator();
+ LocalBuilder pinnedLocal = ilGen.DeclareLocal(typeof(object), pinned: true);
+
+ ilGen.Emit(OpCodes.Ldarg_0); // load 'input' and pin it
+ ilGen.Emit(OpCodes.Stloc, pinnedLocal);
+
+ ilGen.Emit(OpCodes.Ldloc, pinnedLocal); // get the address of field 0 from pinned 'input'
+ ilGen.Emit(OpCodes.Conv_I);
+
+ ilGen.Emit(OpCodes.Call, typeof(RuntimeHelpers).GetProperty("OffsetToStringData").GetMethod); // get pointer to start of string data
+ ilGen.Emit(OpCodes.Add);
+
+ ilGen.Emit(OpCodes.Ldarg_0); // get value of input.GetPinnableReference()
+ ilGen.Emit(OpCodes.Callvirt, typeof(string).GetMethod("GetPinnableReference"));
+
+ // At this point, the top of the evaluation stack is traditional (fixed char* = input) and input.GetPinnableReference().
+ // Compare for equality and return.
+
+ ilGen.Emit(OpCodes.Ceq);
+ ilGen.Emit(OpCodes.Ret);
+
+ Assert.True((bool)dynamicMethod.Invoke(null, new[] { input }));
+ }
+
+ [Fact]
+ public unsafe static void GetPinnableReference_WithNullInput_ThrowsNullRef()
+ {
+ // This test uses an explicit call instead of the normal callvirt that C# would emit.
+ // This allows us to make sure the NullReferenceException is coming from *within*
+ // the GetPinnableReference method rather than on the call site to that method.
+
+ DynamicMethod dynamicMethod = new DynamicMethod("tester", typeof(void), Type.EmptyTypes);
+ ILGenerator ilGen = dynamicMethod.GetILGenerator();
+
+ ilGen.Emit(OpCodes.Ldnull);
+ ilGen.Emit(OpCodes.Call, typeof(string).GetMethod("GetPinnableReference"));
+ ilGen.Emit(OpCodes.Pop);
+ ilGen.Emit(OpCodes.Ret);
+
+ Action del = (Action)dynamicMethod.CreateDelegate(typeof(Action));
+
+ Assert.NotNull(del);
+ Assert.Throws<NullReferenceException>(del);
+ }
+
+ [Theory]
[InlineData("")]
[InlineData("a")]
[InlineData("\0")]