Welcome to mirror list, hosted at ThFree Co, Russian Federation.

ValueType.cs « System « src « System.Private.CoreLib « src - github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 7fad0ee31110db2ed7d9b89b03674cbe45f444b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

/*============================================================
**
**
**
** Purpose: Base class for all value classes.
**
**
===========================================================*/

using System.Runtime;

using Internal.Runtime.CompilerServices;
using Internal.Runtime.Augments;

using Debug = System.Diagnostics.Debug;

namespace System
{
    // CONTRACT with Runtime
    // Place holder type for type hierarchy, Compiler/Runtime requires this class
    [Serializable]
    [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
    public abstract class ValueType
    {
        public override string ToString()
        {
            return this.GetType().ToString();
        }

#if PROJECTN
        public override bool Equals(object obj)
        {
            return RuntimeAugments.Callbacks.ValueTypeEqualsUsingReflection(this, obj);
        }

        public override int GetHashCode()
        {
            return RuntimeAugments.Callbacks.ValueTypeGetHashCodeUsingReflection(this);
        }
#else
        private const int UseFastHelper = -1;
        private const int GetNumFields = -1;

        // An override of this method will be injected by the compiler into all valuetypes that cannot be compared
        // using a simple memory comparison.
        // This API is a bit awkward because we want to avoid burning more than one vtable slot on this.
        // When index == GetNumFields, this method is expected to return the number of fields of this
        // valuetype. Otherwise, it returns the offset and type handle of the index-th field on this type.
        internal virtual int __GetFieldHelper(int index, out EETypePtr eeType)
        {
            // Value types that don't override this method will use the fast path that looks at bytes, not fields.
            Debug.Assert(index == GetNumFields);
            eeType = default;
            return UseFastHelper;
        }

        public override bool Equals(object obj)
        {
            if (obj == null || obj.EETypePtr != this.EETypePtr)
                return false;

            int numFields = __GetFieldHelper(GetNumFields, out _);

            ref byte thisRawData = ref this.GetRawData();
            ref byte thatRawData = ref obj.GetRawData();

            if (numFields == UseFastHelper)
            {
                // Sanity check - if there are GC references, we should not be comparing bytes
                Debug.Assert(!this.EETypePtr.HasPointers);

                // Compare the memory
                int valueTypeSize = (int)this.EETypePtr.ValueTypeSize;
                for (int i = 0; i < valueTypeSize; i++)
                {
                    if (Unsafe.Add(ref thisRawData, i) != Unsafe.Add(ref thatRawData, i))
                        return false;
                }
            }
            else
            {
                // Foreach field, box and call the Equals method.
                for (int i = 0; i < numFields; i++)
                {
                    int fieldOffset = __GetFieldHelper(i, out EETypePtr fieldType);

                    // Fetch the value of the field on both types
                    object thisField = RuntimeImports.RhBoxAny(ref Unsafe.Add(ref thisRawData, fieldOffset), fieldType);
                    object thatField = RuntimeImports.RhBoxAny(ref Unsafe.Add(ref thatRawData, fieldOffset), fieldType);

                    // Compare the fields
                    if (thisField == null)
                    {
                        if (thatField != null)
                            return false;
                    }
                    else if (!thisField.Equals(thatField))
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        public override int GetHashCode()
        {
            int hashCode = this.EETypePtr.GetHashCode();

            hashCode ^= GetHashCodeImpl();

            return hashCode;
        }

        private int GetHashCodeImpl()
        {
            int numFields = __GetFieldHelper(GetNumFields, out _);

            if (numFields == UseFastHelper)
                return FastGetValueTypeHashCodeHelper(this.EETypePtr, ref this.GetRawData());

            return RegularGetValueTypeHashCode(this.EETypePtr, ref this.GetRawData(), numFields);
        }

        private static int FastGetValueTypeHashCodeHelper(EETypePtr type, ref byte data)
        {
            // Sanity check - if there are GC references, we should not be hashing bytes
            Debug.Assert(!type.HasPointers);

            int size = (int)type.ValueTypeSize;
            int hashCode = 0;

            for (int i = 0; i < size / 4; i++)
            {
                hashCode ^= Unsafe.As<byte, int>(ref Unsafe.Add(ref data, i * 4));
            }

            return hashCode;
        }

        private int RegularGetValueTypeHashCode(EETypePtr type, ref byte data, int numFields)
        {
            int hashCode = 0;

            // We only take the hashcode for the first non-null field. That's what the CLR does.
            for (int i = 0; i < numFields; i++)
            {
                int fieldOffset = __GetFieldHelper(i, out EETypePtr fieldType);
                ref byte fieldData = ref Unsafe.Add(ref data, fieldOffset);

                Debug.Assert(!fieldType.IsPointer);

                if (fieldType.CorElementType == RuntimeImports.RhCorElementType.ELEMENT_TYPE_R4)
                {
                    hashCode = Unsafe.Read<float>(ref fieldData).GetHashCode();
                }
                else if (fieldType.CorElementType == RuntimeImports.RhCorElementType.ELEMENT_TYPE_R8)
                {
                    hashCode = Unsafe.Read<double>(ref fieldData).GetHashCode();
                }
                else if (fieldType.IsPrimitive)
                {
                    hashCode = FastGetValueTypeHashCodeHelper(fieldType, ref fieldData);
                }
                else if (fieldType.IsValueType)
                {
                    // We have no option but to box since this value type could have
                    // GC pointers (we could find out if we want though), or fields of type Double/Single (we can't
                    // really find out). Double/Single have weird requirements around -0.0 and +0.0.
                    // If this boxing becomes a problem, we could build a piece of infrastructure that determines the slot
                    // of __GetFieldHelper, decodes the unboxing stub pointed to by the slot to the real target
                    // (we already have that part), and calls the entrypoint that expects a byref `this`, and use the
                    // data to decide between calling fast or regular hashcode helper.
                    var fieldValue = (ValueType)RuntimeImports.RhBox(fieldType, ref fieldData);
                    hashCode = fieldValue.GetHashCodeImpl();
                }
                else
                {
                    object fieldValue = Unsafe.Read<object>(ref fieldData);
                    if (fieldValue != null)
                    {
                        hashCode = fieldValue.GetHashCode();
                    }
                    else
                    {
                        // null object reference, try next
                        continue;
                    }
                }
                break;
            }

            return hashCode;
        }
#endif
    }
}