Adding Unsafe.SkipInit (dotnet/corefx#41995)
authorTanner Gooding <tagoo@outlook.com>
Tue, 22 Oct 2019 22:53:01 +0000 (15:53 -0700)
committerGitHub <noreply@github.com>
Tue, 22 Oct 2019 22:53:01 +0000 (15:53 -0700)
* Adding Unsafe.SkipInit

* Removing the "where T : struct" constraint from Unsafe.SkipInit

* Adding tests for Unsafe.SkipInit

Commit migrated from https://github.com/dotnet/corefx/commit/cd97cf573c9c30f56437d0c05c489a8e1fa97c4b

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

index 4fa818b..d010d53 100644 (file)
@@ -39,6 +39,7 @@ namespace System.Runtime.CompilerServices
         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; }
+        public static void SkipInit<T>(out T value) { throw null; }
         public static int SizeOf<T>() { throw null; }
         public static ref T SubtractByteOffset<T>(ref T source, System.IntPtr byteOffset) { throw null; }
         public static unsafe void* Subtract<T>(void* source, int elementOffset) { throw null; }
index d25aee8..ced09b6 100644 (file)
@@ -6,12 +6,12 @@
 
 .assembly System.Runtime.CompilerServices.Unsafe
 {
-  .custom instance void [CORE_ASSEMBLY]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 
+  .custom instance void [CORE_ASSEMBLY]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
   .custom instance void [CORE_ASSEMBLY]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
                                                                                                                    63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.
 
   // --- The following custom attribute is added automatically, do not uncomment -------
-  //  .custom instance void [CORE_ASSEMBLY]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [CORE_ASSEMBLY]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) 
+  //  .custom instance void [CORE_ASSEMBLY]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [CORE_ASSEMBLY]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 )
 
   .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 34 2E 30 2E 30 2E 30 00 00 )             // ...4.0.0.0..
   .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyInformationalVersionAttribute::.ctor(string) = ( 01 00 07 34 2E 30 2E 30 2E 30 00 00 )             // ...4.0.0.0..
@@ -29,7 +29,7 @@
     01 00 0b 53 65 72 76 69 63 65 61 62 6c 65 04 54
     72 75 65 00 00
   ) // "Serviceable", "True"
-  .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 2F C2 A9 20 4D 69 63 72 6F 73 6F 66 74 20   // ../.. Microsoft 
+  .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 2F C2 A9 20 4D 69 63 72 6F 73 6F 66 74 20   // ../.. Microsoft
                                                                                         43 6F 72 70 6F 72 61 74 69 6F 6E 2E 20 20 41 6C   // Corporation.  Al
                                                                                         6C 20 72 69 67 68 74 73 20 72 65 73 65 72 76 65   // l rights reserve
                                                                                         64 2E 00 00 )                                     // d...
   {
         .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 )
         .maxstack 1
-        ldarg.0        
+        ldarg.0
         unaligned. 0x1
         ldobj !!T
         ret
   } // end of method Unsafe::ReadUnaligned
-  
+
   .method public hidebysig static !!T ReadUnaligned<T>(uint8& source) cil managed aggressiveinlining
   {
         .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 )
         .maxstack 1
-        ldarg.0        
+        ldarg.0
         unaligned. 0x1
         ldobj !!T
         ret
   } // end of method Unsafe::ReadUnaligned
-  
+
   .method public hidebysig static void Write<T>(void* destination,
                                                  !!T 'value') cil managed aggressiveinlining
   {
         .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 )
         .maxstack 2
         ldarg.0
-        ldarg.1        
+        ldarg.1
         unaligned. 0x01
         stobj !!T
         ret
         ret
   } // end of method Unsafe::AsPointer
 
+  .method public hidebysig static void SkipInit<T> ([out] !!T& 'value') cil managed aggressiveinlining
+  {
+        .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 )
+        .maxstack 0
+        ret
+  } // end of method Unsafe::SkipInit
+
   .method public hidebysig static int32 SizeOf<T>() cil managed aggressiveinlining
   {
         .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 )
         ldarg.0
         ldarg.1
         ceq
-        ret 
+        ret
   } // end of method Unsafe::AreSame
 
   .method public hidebysig static bool IsAddressGreaterThan<T>(!!T& left, !!T& right) cil managed aggressiveinlining
         ldarg.0
         ldarg.1
         cgt.un
-        ret 
+        ret
   } // end of method Unsafe::IsAddressGreaterThan
 
   .method public hidebysig static bool IsAddressLessThan<T>(!!T& left, !!T& right) cil managed aggressiveinlining
         ldarg.0
         ldarg.1
         clt.un
-        ret 
+        ret
   } // end of method Unsafe::IsAddressLessThan
 
 } // end of class System.Runtime.CompilerServices.Unsafe
   .custom instance void [CORE_ASSEMBLY]System.AttributeUsageAttribute::.ctor(valuetype [CORE_ASSEMBLY]System.AttributeTargets) = ( 01 00 6C 00 00 00 02 00 54 02 0D 41 6C 6C 6F 77   // ..l.....T..Allow
                                                                                                                                      4D 75 6C 74 69 70 6C 65 00 54 02 09 49 6E 68 65   // Multiple.T..Inhe
                                                                                                                                      72 69 74 65 64 00 )                               // rited.
-  .method public hidebysig specialname rtspecialname 
+  .method public hidebysig specialname rtspecialname
           instance void  .ctor() cil managed
   {
         .maxstack 1
 .class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsReadOnlyAttribute
        extends [CORE_ASSEMBLY]System.Attribute
 {
-  .method public hidebysig specialname rtspecialname 
-          instance void .ctor () cil managed 
+  .method public hidebysig specialname rtspecialname
+          instance void .ctor () cil managed
   {
         .maxstack 1
         ldarg.0
index a3057b9..9119a7e 100644 (file)
@@ -822,6 +822,116 @@ namespace System.Runtime.CompilerServices
 
             Assert.Throws<InvalidCastException>(() => Unsafe.Unbox<bool>(box));
         }
+
+        [Fact]
+        public static void SkipInit()
+        {
+            // Validate that calling with primitive types works.
+
+            Unsafe.SkipInit(out sbyte sbyteValue);
+            Unsafe.SkipInit(out byte byteValue);
+            Unsafe.SkipInit(out short shortValue);
+            Unsafe.SkipInit(out ushort ushortValue);
+            Unsafe.SkipInit(out int intValue);
+            Unsafe.SkipInit(out uint uintValue);
+            Unsafe.SkipInit(out long longValue);
+            Unsafe.SkipInit(out ulong ulongValue);
+            Unsafe.SkipInit(out float floatValue);
+            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);
+            Unsafe.SkipInit(out Int32Double int32DoubleValue);
+
+            // Validates that calling on a struct works and the reference type is still zeroed.
+
+            Unsafe.SkipInit(out StringInt32 stringInt32Value);
+            Assert.Null(stringInt32Value.String);
+
+            // Validates that calling on a reference type works and it is zeroed.
+
+            Unsafe.SkipInit(out string stringValue);
+            Assert.Null(stringValue);
+        }
+
+        [Fact]
+        public static void SkipInit_PreservesPrevious()
+        {
+            // Validate that calling on already initialized types preserves the previous value.
+
+            sbyte sbyteValue = 1;
+            Unsafe.SkipInit(out sbyteValue);
+            Assert.Equal<sbyte>(1, sbyteValue);
+
+            byte byteValue = 2;
+            Unsafe.SkipInit(out byteValue);
+            Assert.Equal<byte>(2, byteValue);
+
+            short shortValue = 3;
+            Unsafe.SkipInit(out shortValue);
+            Assert.Equal<short>(3, shortValue);
+
+            ushort ushortValue = 4;
+            Unsafe.SkipInit(out ushortValue);
+            Assert.Equal<ushort>(4, ushortValue);
+
+            int intValue = 5;
+            Unsafe.SkipInit(out intValue);
+            Assert.Equal<int>(5, intValue);
+
+            uint uintValue = 6;
+            Unsafe.SkipInit(out uintValue);
+            Assert.Equal<uint>(6, uintValue);
+
+            long longValue = 7;
+            Unsafe.SkipInit(out longValue);
+            Assert.Equal<long>(7, longValue);
+
+            ulong ulongValue = 8;
+            Unsafe.SkipInit(out ulongValue);
+            Assert.Equal<ulong>(8, ulongValue);
+
+            float floatValue = 9;
+            Unsafe.SkipInit(out floatValue);
+            Assert.Equal<float>(9, floatValue);
+
+            double doubleValue = 10;
+            Unsafe.SkipInit(out doubleValue);
+            Assert.Equal<double>(10, doubleValue);
+
+            Byte4 byte4Value = new Byte4 { B0 = 11, B1 = 12, B2 = 13, B3 = 14 };
+            Unsafe.SkipInit(out byte4Value);
+            Assert.Equal<byte>(11, byte4Value.B0);
+            Assert.Equal<byte>(12, byte4Value.B1);
+            Assert.Equal<byte>(13, byte4Value.B2);
+            Assert.Equal<byte>(14, byte4Value.B3);
+
+            Byte4Short2 byte4Short2Value = new Byte4Short2 { B0 = 15, B1 = 16, B2 = 17, B3 = 18, S4 = 19, S6 = 20 };
+            Unsafe.SkipInit(out byte4Short2Value);
+            Assert.Equal<byte>(15, byte4Short2Value.B0);
+            Assert.Equal<byte>(16, byte4Short2Value.B1);
+            Assert.Equal<byte>(17, byte4Short2Value.B2);
+            Assert.Equal<byte>(18, byte4Short2Value.B3);
+            Assert.Equal<short>(19, byte4Short2Value.S4);
+            Assert.Equal<short>(20, byte4Short2Value.S6);
+
+            Int32Double int32DoubleValue = new Int32Double { Int32 = 21, Double = 22 };
+            Unsafe.SkipInit(out int32DoubleValue);
+            Assert.Equal<int>(21, int32DoubleValue.Int32);
+            Assert.Equal<double>(22, int32DoubleValue.Double);
+
+            StringInt32 stringInt32Value = new StringInt32 { String = "23", Int32 = 24 };
+            Unsafe.SkipInit(out stringInt32Value);
+            Assert.Equal("23", stringInt32Value.String);
+            Assert.Equal<int>(24, stringInt32Value.Int32);
+
+            string stringValue = "25";
+            Unsafe.SkipInit(out stringValue);
+            Assert.Equal("25", stringValue);
+        }
     }
 
     [StructLayout(LayoutKind.Explicit)]
@@ -892,4 +1002,10 @@ namespace System.Runtime.CompilerServices
             return aligned;
         }
     }
+
+    public struct StringInt32
+    {
+        public string String;
+        public int Int32;
+    }
 }