Skip to content

Commit e671f57

Browse files
Sergio0694am11jkotas
authored
Add Unsafe.IsNullRef and Unsafe.NullRef (#40008)
Co-authored-by: Adeel Mujahid <adeelbm@outlook.com> Co-authored-by: Jan Kotas <jkotas@microsoft.com>
1 parent 26b818b commit e671f57

File tree

4 files changed

+115
-3
lines changed

4 files changed

+115
-3
lines changed

src/libraries/System.Runtime.CompilerServices.Unsafe/ref/System.Runtime.CompilerServices.Unsafe.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public static void InitBlockUnaligned(ref byte startAddress, byte value, uint by
3535
public static unsafe void InitBlockUnaligned(void* startAddress, byte value, uint byteCount) { }
3636
public static bool IsAddressGreaterThan<T>(ref T left, ref T right) { throw null; }
3737
public static bool IsAddressLessThan<T>(ref T left, ref T right) { throw null; }
38+
public static bool IsNullRef<T>(ref T source) { throw null; }
39+
public static ref T NullRef<T>() { throw null; }
3840
public static T ReadUnaligned<T>(ref byte source) { throw null; }
3941
public static unsafe T ReadUnaligned<T>(void* source) { throw null; }
4042
public static unsafe T Read<T>(void* source) { throw null; }

src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,26 @@
458458
ret
459459
} // end of method Unsafe::IsAddressLessThan
460460

461+
.method public hidebysig static bool IsNullRef<T>(!!T& source) cil managed aggressiveinlining
462+
{
463+
.custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 )
464+
.maxstack 2
465+
ldarg.0
466+
ldc.i4.0
467+
conv.u
468+
ceq
469+
ret
470+
} // end of method Unsafe::IsNullRef
471+
472+
.method public hidebysig static !!T& NullRef<T>() cil managed aggressiveinlining
473+
{
474+
.custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 )
475+
.maxstack 1
476+
ldc.i4.0
477+
conv.u
478+
ret
479+
} // end of method Unsafe::NullRef
480+
461481
} // end of class System.Runtime.CompilerServices.Unsafe
462482

463483
.class private auto ansi sealed beforefieldinit System.Runtime.Versioning.NonVersionableAttribute

src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ public static void ByteOffsetStackByte4()
413413
public static unsafe void AsRef()
414414
{
415415
byte[] b = new byte[4] { 0x42, 0x42, 0x42, 0x42 };
416-
fixed (byte * p = b)
416+
fixed (byte* p = b)
417417
{
418418
ref int r = ref Unsafe.AsRef<int>(p);
419419
Assert.Equal(0x42424242, r);
@@ -839,7 +839,7 @@ public static void SkipInit()
839839
Unsafe.SkipInit(out double doubleValue);
840840

841841
// Validate that calling on user-defined unmanaged structs works.
842-
842+
843843
Unsafe.SkipInit(out Byte4 byte4Value);
844844
Unsafe.SkipInit(out Byte4Short2 byte4Short2Value);
845845
Unsafe.SkipInit(out Byte512 byte512Value);
@@ -931,6 +931,84 @@ public static void SkipInit_PreservesPrevious()
931931
Unsafe.SkipInit(out stringValue);
932932
Assert.Equal("25", stringValue);
933933
}
934+
935+
[Fact]
936+
public static unsafe void IsNullRef_NotNull()
937+
{
938+
// Validate that calling with a primitive type works.
939+
940+
int intValue = 5;
941+
Assert.False(Unsafe.IsNullRef<int>(ref intValue));
942+
943+
// Validate that calling on user-defined unmanaged structs works.
944+
945+
Int32Double int32DoubleValue = default;
946+
Assert.False(Unsafe.IsNullRef<Int32Double>(ref int32DoubleValue));
947+
948+
// Validate that calling on reference types works.
949+
950+
object objectValue = new object();
951+
Assert.False(Unsafe.IsNullRef<object>(ref objectValue));
952+
953+
string stringValue = nameof(IsNullRef_NotNull);
954+
Assert.False(Unsafe.IsNullRef<string>(ref stringValue));
955+
956+
// Validate on ref created from a pointer
957+
958+
int* p = (int*)1;
959+
Assert.False(Unsafe.IsNullRef<int>(ref Unsafe.AsRef<int>(p)));
960+
}
961+
962+
[Fact]
963+
public static unsafe void IsNullRef_Null()
964+
{
965+
// Validate that calling with a primitive type works.
966+
967+
Assert.True(Unsafe.IsNullRef<int>(ref Unsafe.AsRef<int>(null)));
968+
969+
// Validate that calling on user-defined unmanaged structs works.
970+
971+
Assert.True(Unsafe.IsNullRef<Int32Double>(ref Unsafe.AsRef<Int32Double>(null)));
972+
973+
// Validate that calling on reference types works.
974+
975+
Assert.True(Unsafe.IsNullRef<object>(ref Unsafe.AsRef<object>(null)));
976+
Assert.True(Unsafe.IsNullRef<string>(ref Unsafe.AsRef<string>(null)));
977+
978+
// Validate on ref created from a pointer
979+
980+
int* p = (int*)0;
981+
Assert.True(Unsafe.IsNullRef<int>(ref Unsafe.AsRef<int>(p)));
982+
}
983+
984+
[Fact]
985+
public static unsafe void NullRef()
986+
{
987+
// Validate that calling with a primitive type works.
988+
989+
Assert.True(Unsafe.IsNullRef<int>(ref Unsafe.NullRef<int>()));
990+
991+
// Validate that calling on user-defined unmanaged structs works.
992+
993+
Assert.True(Unsafe.IsNullRef<Int32Double>(ref Unsafe.NullRef<Int32Double>()));
994+
995+
// Validate that calling on reference types works.
996+
997+
Assert.True(Unsafe.IsNullRef<object>(ref Unsafe.NullRef<object>()));
998+
Assert.True(Unsafe.IsNullRef<string>(ref Unsafe.NullRef<string>()));
999+
1000+
// Validate that pinning results in a null pointer
1001+
1002+
fixed (void* p = &Unsafe.NullRef<int>())
1003+
{
1004+
Assert.True(p == (void*)0);
1005+
}
1006+
1007+
// Validate that dereferencing a null ref throws a NullReferenceException
1008+
1009+
Assert.Throws<NullReferenceException>(() => Unsafe.NullRef<int>() = 42);
1010+
Assert.Throws<NullReferenceException>(() => Unsafe.NullRef<int>());
1011+
}
9341012
}
9351013

9361014
[StructLayout(LayoutKind.Explicit)]
@@ -968,7 +1046,7 @@ public unsafe struct Byte512
9681046
public fixed byte Bytes[512];
9691047
}
9701048

971-
[StructLayout(LayoutKind.Explicit, Size=16)]
1049+
[StructLayout(LayoutKind.Explicit, Size = 16)]
9721050
public unsafe struct Int32Double
9731051
{
9741052
[FieldOffset(0)]

0 commit comments

Comments
 (0)