diff --git a/WoofWare.PawPrint.Test/sourcesPure/Initobj.cs b/WoofWare.PawPrint.Test/sourcesPure/Initobj.cs index 41efc1a..32286a3 100644 --- a/WoofWare.PawPrint.Test/sourcesPure/Initobj.cs +++ b/WoofWare.PawPrint.Test/sourcesPure/Initobj.cs @@ -70,8 +70,8 @@ public class TestInitobj if (s.DoubleField != 3.14) return 4; if (s.ByteField != 255) return 5; - // Use initobj to reset the struct - Unsafe.InitBlockUnaligned(ref Unsafe.As(ref s), 0, (uint)Unsafe.SizeOf()); + // Use default to reset the struct (generates initobj) + s = default(SimpleStruct); // Verify all fields are zeroed if (s.IntField != 0) return 6; @@ -181,11 +181,11 @@ public class TestInitobj return 0; } - // Test 5: Initialize struct in array element + // Test 5: Initialize struct in array element using ref public static int Test5() { SimpleStruct[] array = new SimpleStruct[3]; - + // Set values in first element array[0].IntField = 111; array[0].BoolField = true; @@ -195,14 +195,20 @@ public class TestInitobj if (array[0].BoolField != true) return 51; if (array[0].CharField != 'Z') return 52; - // Reset first element using ref and Unsafe - ref SimpleStruct firstElement = ref array[0]; - Unsafe.InitBlockUnaligned(ref Unsafe.As(ref firstElement), 0, (uint)Unsafe.SizeOf()); + // Reset first element using default assignment + array[0] = default(SimpleStruct); if (array[0].IntField != 0) return 53; if (array[0].BoolField != false) return 54; if (array[0].CharField != '\0') return 55; + // Also test with ref local + array[1].IntField = 222; + ref SimpleStruct secondElement = ref array[1]; + secondElement = default(SimpleStruct); + + if (array[1].IntField != 0) return 56; + return 0; } @@ -286,10 +292,10 @@ public class TestInitobj // Verify initial values if (arg.IntField != 333) return 85; if (arg.BoolField != true) return 86; - + // Reset using default - this should use initobj on the argument arg = default(SimpleStruct); - + return 0; } @@ -376,7 +382,7 @@ public class TestInitobj // Get a pointer to the struct SimpleStruct* ptr = &s; - + // Initialize through pointer *ptr = default(SimpleStruct); @@ -390,6 +396,63 @@ public class TestInitobj return 0; } + // Test 12: Initialize struct through Unsafe.AsRef + public static int Test12() + { + SimpleStruct s = new SimpleStruct + { + IntField = 999, + BoolField = true, + CharField = 'U', + DoubleField = 12.34, + ByteField = 200 + }; + + // Use Unsafe to get a ref and initialize it + ref SimpleStruct sRef = ref s; + sRef = default(SimpleStruct); + + // Verify all fields are zeroed + if (s.IntField != 0) return 120; + if (s.BoolField != false) return 121; + if (s.CharField != '\0') return 122; + if (s.DoubleField != 0.0) return 123; + if (s.ByteField != 0) return 124; + + return 0; + } + + // Test 13: Initialize readonly struct + public static int Test13() + { + ReadonlyStruct ros = new ReadonlyStruct(100, true); + + // Verify initial values through properties + if (ros.IntValue != 100) return 130; + if (ros.BoolValue != true) return 131; + + // Reset using default + ros = default(ReadonlyStruct); + + // Verify zeroed + if (ros.IntValue != 0) return 132; + if (ros.BoolValue != false) return 133; + + return 0; + } + + private readonly struct ReadonlyStruct + { + public readonly int IntValue; + public readonly bool BoolValue; + + public ReadonlyStruct(int i, bool b) + { + IntValue = i; + BoolValue = b; + } + } + public static int Main(string[] argv) { var result = Test1(); @@ -425,7 +488,13 @@ public class TestInitobj result = Test11(); if (result != 0) return result; + result = Test12(); + if (result != 0) return result; + + result = Test13(); + if (result != 0) return result; + // All tests passed return 0; } -} \ No newline at end of file +} diff --git a/WoofWare.PawPrint/Intrinsics.fs b/WoofWare.PawPrint/Intrinsics.fs index 9916a0c..c0240ae 100644 --- a/WoofWare.PawPrint/Intrinsics.fs +++ b/WoofWare.PawPrint/Intrinsics.fs @@ -43,6 +43,8 @@ module Intrinsics = None else + // In general, some implementations are in: + // https://github.com/dotnet/runtime/blob/108fa7856efcfd39bc991c2d849eabbf7ba5989c/src/coreclr/tools/Common/TypeSystem/IL/Stubs/UnsafeIntrinsics.cs#L192 match methodToCall.DeclaringType.Assembly.Name, methodToCall.DeclaringType.Name, methodToCall.Name with | "System.Private.CoreLib", "Type", "get_TypeHandle" -> // TODO: check return type is RuntimeTypeHandle @@ -321,6 +323,7 @@ module Intrinsics = | [ from ; to_ ] -> from, to_ | _ -> failwith "bad generics" + failwith "TODO: transmute fields etc" let state = state |> IlMachineState.advanceProgramCounter currentThread Some state @@ -343,5 +346,8 @@ module Intrinsics = |> IlMachineState.pushToEvalStack (CliType.Numeric (CliNumericType.Int32 size)) currentThread |> IlMachineState.advanceProgramCounter currentThread |> Some + | "System.Private.CoreLib", "ReadOnlySpan`1", "get_Length" -> + // https://github.com/dotnet/runtime/blob/108fa7856efcfd39bc991c2d849eabbf7ba5989c/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs#L161 + None | a, b, c -> failwith $"TODO: implement JIT intrinsic {a}.{b}.{c}" |> Option.map (fun s -> s.WithThreadSwitchedToAssembly callerAssy currentThread |> fst)