Skip to content

Commit

Permalink
Add Unsafe.IsNullRef and Unsafe.NullRef (#40008)
Browse files Browse the repository at this point in the history
Co-authored-by: Adeel Mujahid <adeelbm@outlook.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
  • Loading branch information
3 people authored Jul 29, 2020
1 parent 26b818b commit e671f57
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public static void InitBlockUnaligned(ref byte startAddress, byte value, uint by
public static unsafe void InitBlockUnaligned(void* startAddress, byte value, uint byteCount) { }
public static bool IsAddressGreaterThan<T>(ref T left, ref T right) { throw null; }
public static bool IsAddressLessThan<T>(ref T left, ref T right) { throw null; }
public static bool IsNullRef<T>(ref T source) { throw null; }
public static ref T NullRef<T>() { throw null; }
public static T ReadUnaligned<T>(ref byte source) { throw null; }
public static unsafe T ReadUnaligned<T>(void* source) { throw null; }
public static unsafe T Read<T>(void* source) { throw null; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,26 @@
ret
} // end of method Unsafe::IsAddressLessThan

.method public hidebysig static bool IsNullRef<T>(!!T& source) cil managed aggressiveinlining
{
.custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 )
.maxstack 2
ldarg.0
ldc.i4.0
conv.u
ceq
ret
} // end of method Unsafe::IsNullRef

.method public hidebysig static !!T& NullRef<T>() cil managed aggressiveinlining
{
.custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 )
.maxstack 1
ldc.i4.0
conv.u
ret
} // end of method Unsafe::NullRef

} // end of class System.Runtime.CompilerServices.Unsafe

.class private auto ansi sealed beforefieldinit System.Runtime.Versioning.NonVersionableAttribute
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ public static void ByteOffsetStackByte4()
public static unsafe void AsRef()
{
byte[] b = new byte[4] { 0x42, 0x42, 0x42, 0x42 };
fixed (byte * p = b)
fixed (byte* p = b)
{
ref int r = ref Unsafe.AsRef<int>(p);
Assert.Equal(0x42424242, r);
Expand Down Expand Up @@ -839,7 +839,7 @@ public static void SkipInit()
Unsafe.SkipInit(out double doubleValue);

// Validate that calling on user-defined unmanaged structs works.

Unsafe.SkipInit(out Byte4 byte4Value);
Unsafe.SkipInit(out Byte4Short2 byte4Short2Value);
Unsafe.SkipInit(out Byte512 byte512Value);
Expand Down Expand Up @@ -931,6 +931,84 @@ public static void SkipInit_PreservesPrevious()
Unsafe.SkipInit(out stringValue);
Assert.Equal("25", stringValue);
}

[Fact]
public static unsafe void IsNullRef_NotNull()
{
// Validate that calling with a primitive type works.

int intValue = 5;
Assert.False(Unsafe.IsNullRef<int>(ref intValue));

// Validate that calling on user-defined unmanaged structs works.

Int32Double int32DoubleValue = default;
Assert.False(Unsafe.IsNullRef<Int32Double>(ref int32DoubleValue));

// Validate that calling on reference types works.

object objectValue = new object();
Assert.False(Unsafe.IsNullRef<object>(ref objectValue));

string stringValue = nameof(IsNullRef_NotNull);
Assert.False(Unsafe.IsNullRef<string>(ref stringValue));

// Validate on ref created from a pointer

int* p = (int*)1;
Assert.False(Unsafe.IsNullRef<int>(ref Unsafe.AsRef<int>(p)));
}

[Fact]
public static unsafe void IsNullRef_Null()
{
// Validate that calling with a primitive type works.

Assert.True(Unsafe.IsNullRef<int>(ref Unsafe.AsRef<int>(null)));

// Validate that calling on user-defined unmanaged structs works.

Assert.True(Unsafe.IsNullRef<Int32Double>(ref Unsafe.AsRef<Int32Double>(null)));

// Validate that calling on reference types works.

Assert.True(Unsafe.IsNullRef<object>(ref Unsafe.AsRef<object>(null)));
Assert.True(Unsafe.IsNullRef<string>(ref Unsafe.AsRef<string>(null)));

// Validate on ref created from a pointer

int* p = (int*)0;
Assert.True(Unsafe.IsNullRef<int>(ref Unsafe.AsRef<int>(p)));
}

[Fact]
public static unsafe void NullRef()
{
// Validate that calling with a primitive type works.

Assert.True(Unsafe.IsNullRef<int>(ref Unsafe.NullRef<int>()));

// Validate that calling on user-defined unmanaged structs works.

Assert.True(Unsafe.IsNullRef<Int32Double>(ref Unsafe.NullRef<Int32Double>()));

// Validate that calling on reference types works.

Assert.True(Unsafe.IsNullRef<object>(ref Unsafe.NullRef<object>()));
Assert.True(Unsafe.IsNullRef<string>(ref Unsafe.NullRef<string>()));

// Validate that pinning results in a null pointer

fixed (void* p = &Unsafe.NullRef<int>())
{
Assert.True(p == (void*)0);
}

// Validate that dereferencing a null ref throws a NullReferenceException

Assert.Throws<NullReferenceException>(() => Unsafe.NullRef<int>() = 42);
Assert.Throws<NullReferenceException>(() => Unsafe.NullRef<int>());
}
}

[StructLayout(LayoutKind.Explicit)]
Expand Down Expand Up @@ -968,7 +1046,7 @@ public unsafe struct Byte512
public fixed byte Bytes[512];
}

[StructLayout(LayoutKind.Explicit, Size=16)]
[StructLayout(LayoutKind.Explicit, Size = 16)]
public unsafe struct Int32Double
{
[FieldOffset(0)]
Expand Down

0 comments on commit e671f57

Please sign in to comment.