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

Attribute.cs « System « src « System.Private.CoreLib « src - github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 8c48f16e30088b3ee7aa6267934c5f9d14a5dc65 (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
// 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.

using System.Diagnostics;

namespace System
{
    [AttributeUsageAttribute(AttributeTargets.All, Inherited = true, AllowMultiple = false)]
    [Serializable]
    [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
    public abstract partial class Attribute
    {
        protected Attribute() { }

        //
        // Compat note: .NET Core changed the behavior of Equals() relative to the full framework:
        //
        //    (https://github.com/dotnet/coreclr/pull/6240)
        //
        // This implementation implements the .NET Core behavior.
        //
        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;

            if (this.GetType() != obj.GetType())
                return false;

            object[] thisFieldValues = this.ReadFields();
            object[] thatfieldValues = ((Attribute)obj).ReadFields();

            for (int i = 0; i < thisFieldValues.Length; i++)
            {
                // Visibility check and consistency check are not necessary.
                object thisResult = thisFieldValues[i];
                object thatResult = thatfieldValues[i];

                if (!AreFieldValuesEqual(thisResult, thatResult))
                {
                    return false;
                }
            }

            return true;
        }

        // Compares values of custom-attribute fields.    
        private static bool AreFieldValuesEqual(object thisValue, object thatValue)
        {
            if (thisValue == null && thatValue == null)
                return true;
            if (thisValue == null || thatValue == null)
                return false;

            Type thisValueType = thisValue.GetType();

            if (thisValueType.IsArray)
            {
                // Ensure both are arrays of the same type.
                if (!thisValueType.Equals(thatValue.GetType()))
                {
                    return false;
                }

                Array thisValueArray = thisValue as Array;
                Array thatValueArray = thatValue as Array;
                if (thisValueArray.Length != thatValueArray.Length)
                {
                    return false;
                }

                // Attributes can only contain single-dimension arrays, so we don't need to worry about 
                // multidimensional arrays.
                Debug.Assert(thisValueArray.Rank == 1 && thatValueArray.Rank == 1);
                for (int j = 0; j < thisValueArray.Length; j++)
                {
                    if (!AreFieldValuesEqual(thisValueArray.GetValue(j), thatValueArray.GetValue(j)))
                    {
                        return false;
                    }
                }
            }
            else
            {
                // An object of type Attribute will cause a stack overflow. 
                // However, this should never happen because custom attributes cannot contain values other than
                // constants, single-dimensional arrays and typeof expressions.
                Debug.Assert(!(thisValue is Attribute));
                if (!thisValue.Equals(thatValue))
                    return false;
            }

            return true;
        }

        public override int GetHashCode()
        {
            object vThis = null;

            object[] fieldValues = this.ReadFields();
            for (int i = 0; i < fieldValues.Length; i++)
            {
                object fieldValue = fieldValues[i];

                // The hashcode of an array ignores the contents of the array, so it can produce 
                // different hashcodes for arrays with the same contents.
                // Since we do deep comparisons of arrays in Equals(), this means Equals and GetHashCode will
                // be inconsistent for arrays. Therefore, we ignore hashes of arrays.
                if (fieldValue != null && !fieldValue.GetType().IsArray)
                    vThis = fieldValue;

                if (vThis != null)
                    break;
            }

            if (vThis != null)
                return vThis.GetHashCode();

            Type type = GetType();

            return type.GetHashCode();
        }

        public virtual object TypeId => GetType();

        public virtual bool Match(object obj) => Equals(obj);

        public virtual bool IsDefaultAttribute() => false;

        //
        // This non-contract method is known to the IL transformer. See comments around _ILT_ReadFields() for more detail.
        //
        [CLSCompliant(false)]
        protected virtual int _ILT_GetNumFields()
        {
            return 0;
        }

        //
        // This non-contract method is known to the IL transformer. The IL transformer generates an override of this for each specific Attribute class.
        // Together with _ILT_GetNumFields(), it fetches the same field values that the desktop would have for comparison.
        //
        // .NET Core uses "GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)" to
        // determine the list of fields used for comparison. Unfortunately, this list can include fields that the "this" class has no right to access (e.g. "internal"
        // fields in base classes defined in another assembly.) Thus, the IL Transformer cannot simply generate a method to walk the fields and
        // be done with it. Instead, _ILT_ReadFields() directly fetches only the directly declared fields and reinvokes itself non-virtually on its
        // base class to get any inherited fields. To simplify the IL generation, the generated method only writes the results into a specified
        // offset inside a caller-supplied array. Attribute.ReadFields() calls _ILT_GetNumFields() to figure out how large an array is needed.
        //
        [CLSCompliant(false)]
        protected virtual void _ILT_ReadFields(object[] destination, int offset)
        {
        }

        //
        // ProjectN: Unlike the desktop, low level code such as Attributes cannot go running off to Reflection to fetch the FieldInfo's.
        // Instead, we use the ILTransform to generate a method that returns the relevant field values, which we then compare as the desktop does.
        //
        private object[] ReadFields()
        {
            int numFields = _ILT_GetNumFields();
            object[] fieldValues = new object[numFields];
            _ILT_ReadFields(fieldValues, 0);
            return fieldValues;
        }
    }
}