1
+ using Microsoft . Diagnostics . Runtime ;
2
+
3
+ namespace Heartbeat . Runtime . Extensions
4
+ {
5
+ public static class ClrValueTypeExtensions
6
+ {
7
+ public static bool IsDefaultValue ( this ClrValueType valueType )
8
+ {
9
+ if ( valueType . Type == null )
10
+ {
11
+ return true ;
12
+ }
13
+
14
+ foreach ( var field in valueType . Type . Fields )
15
+ {
16
+ if ( field . IsObjectReference )
17
+ {
18
+ if ( ! field . ReadObject ( valueType . Address , true ) . IsNull )
19
+ {
20
+ return false ;
21
+ }
22
+ }
23
+ else if ( field . IsPrimitive )
24
+ {
25
+ if ( ! IsValueDefault ( valueType . Address , field ) )
26
+ {
27
+ return false ;
28
+ }
29
+ }
30
+ else if ( field . ElementType == ClrElementType . Struct )
31
+ {
32
+ var fieldValue = field . ReadStruct ( valueType . Address , true ) ;
33
+ if ( ! fieldValue . IsDefaultValue ( ) )
34
+ {
35
+ return false ;
36
+ }
37
+ }
38
+ else if ( field . ElementType == ClrElementType . Pointer )
39
+ {
40
+ if ( ! IsZeroPtr ( valueType . Address , field ) )
41
+ {
42
+ return false ;
43
+ }
44
+ }
45
+ else
46
+ {
47
+ throw new InvalidOperationException (
48
+ "Unexpected field, it non of IsObjectReference | IsValueType | IsPrimitive" ) ;
49
+ }
50
+ }
51
+
52
+ return true ;
53
+ }
54
+
55
+ private static bool IsValueDefault ( ulong objRef , ClrInstanceField field )
56
+ {
57
+ return field . ElementType switch
58
+ {
59
+ ClrElementType . Boolean => field . Read < bool > ( objRef , true ) == false ,
60
+ ClrElementType . Char => field . Read < char > ( objRef , true ) == ( char ) 0 ,
61
+ ClrElementType . Int8 => field . Read < sbyte > ( objRef , true ) == ( sbyte ) 0 ,
62
+ ClrElementType . UInt8 => field . Read < byte > ( objRef , true ) == ( byte ) 0 ,
63
+ ClrElementType . Int16 => field . Read < short > ( objRef , true ) == ( short ) 0 ,
64
+ ClrElementType . UInt16 => field . Read < ushort > ( objRef , true ) == ( ushort ) 0 ,
65
+ ClrElementType . Int32 => field . Read < int > ( objRef , true ) == 0 ,
66
+ ClrElementType . UInt32 => field . Read < int > ( objRef , true ) == ( uint ) 0 ,
67
+ ClrElementType . Int64 => field . Read < long > ( objRef , true ) == 0L ,
68
+ ClrElementType . UInt64 => field . Read < ulong > ( objRef , true ) == 0UL ,
69
+ ClrElementType . Float => field . Read < float > ( objRef , true ) == 0f ,
70
+ ClrElementType . Double => field . Read < double > ( objRef , true ) == 0d ,
71
+ ClrElementType . NativeInt => field . Read < nint > ( objRef , true ) == nint . Zero ,
72
+ ClrElementType . NativeUInt => field . Read < nuint > ( objRef , true ) == nuint . Zero ,
73
+ _ => throw new ArgumentOutOfRangeException ( )
74
+ } ;
75
+ }
76
+
77
+ private static bool IsZeroPtr ( ulong objRef , ClrInstanceField field )
78
+ {
79
+ return field . Type . Name switch
80
+ {
81
+ "System.UIntPtr" => field . Read < UIntPtr > ( objRef , true ) == UIntPtr . Zero ,
82
+ "System.IntPtr" => field . Read < IntPtr > ( objRef , true ) == IntPtr . Zero ,
83
+ _ => throw new ArgumentException ( $ "Unknown Pointer type: { field . Type . Name } ")
84
+ } ;
85
+ }
86
+ }
87
+ }
0 commit comments