1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Diagnostics;
6 using System.Reflection.Metadata.Ecma335;
7 
8 namespace System.Reflection.Metadata
9 {
10     public readonly struct CustomAttribute
11     {
12         private readonly MetadataReader _reader;
13 
14         // Workaround: JIT doesn't generate good code for nested structures, so use RowId.
15         private readonly uint _treatmentAndRowId;
16 
CustomAttributeSystem.Reflection.Metadata.CustomAttribute17         internal CustomAttribute(MetadataReader reader, uint treatmentAndRowId)
18         {
19             Debug.Assert(reader != null);
20             Debug.Assert(treatmentAndRowId != 0);
21 
22             _reader = reader;
23             _treatmentAndRowId = treatmentAndRowId;
24         }
25 
26         private int RowId
27         {
28             get { return (int)(_treatmentAndRowId & TokenTypeIds.RIDMask); }
29         }
30 
31         private CustomAttributeHandle Handle
32         {
33             get { return CustomAttributeHandle.FromRowId(RowId); }
34         }
35 
36         private MethodDefTreatment Treatment
37         {
38             get { return (MethodDefTreatment)(_treatmentAndRowId >> TokenTypeIds.RowIdBitCount); }
39         }
40 
41         /// <summary>
42         /// The constructor (<see cref="MethodDefinitionHandle"/> or <see cref="MemberReferenceHandle"/>) of the custom attribute type.
43         /// </summary>
44         /// <remarks>
45         /// Corresponds to Type field of CustomAttribute table in ECMA-335 Standard.
46         /// </remarks>
47         public EntityHandle Constructor
48         {
49             get
50             {
51                 return _reader.CustomAttributeTable.GetConstructor(Handle);
52             }
53         }
54 
55         /// <summary>
56         /// The handle of the metadata entity the attribute is applied to.
57         /// </summary>
58         /// <remarks>
59         /// Corresponds to Parent field of CustomAttribute table in ECMA-335 Standard.
60         /// </remarks>
61         public EntityHandle Parent
62         {
63             get
64             {
65                 return _reader.CustomAttributeTable.GetParent(Handle);
66             }
67         }
68 
69         /// <summary>
70         /// The value of the attribute.
71         /// </summary>
72         /// <remarks>
73         /// Corresponds to Value field of CustomAttribute table in ECMA-335 Standard.
74         /// </remarks>
75         public BlobHandle Value
76         {
77             get
78             {
79                 if (Treatment == 0)
80                 {
81                     return _reader.CustomAttributeTable.GetValue(Handle);
82                 }
83 
84                 return GetProjectedValue();
85             }
86         }
87 
88         /// <summary>
89         /// Decodes the arguments encoded in the value blob.
90         /// </summary>
DecodeValueSystem.Reflection.Metadata.CustomAttribute91         public CustomAttributeValue<TType> DecodeValue<TType>(ICustomAttributeTypeProvider<TType> provider)
92         {
93             var decoder = new CustomAttributeDecoder<TType>(provider, _reader);
94             return decoder.DecodeValue(Constructor, Value);
95         }
96 
97         #region Projections
98 
GetProjectedValueSystem.Reflection.Metadata.CustomAttribute99         private BlobHandle GetProjectedValue()
100         {
101             // The usual pattern for accessing custom attributes differs from pattern for accessing e.g. TypeDef row fields.
102             // The value blob is only accessed when the consumer is about to decode it (which is a nontrivial process),
103             // while the Constructor and Parent fields are often accessed when searching for a particular attribute.
104             //
105             // The current WinMD projections only affect the blob and not the Constructor and Parent values.
106             // It is thus more efficient to calculate the treatment here (and make GetValue more expensive) and
107             // avoid calculating the treatment when the CustomAttributeHandle is looked up and CustomAttribute struct
108             // is initialized.
109 
110             CustomAttributeValueTreatment treatment = _reader.CalculateCustomAttributeValueTreatment(Handle);
111             if (treatment == 0)
112             {
113                 return _reader.CustomAttributeTable.GetValue(Handle);
114             }
115 
116             return GetProjectedValue(treatment);
117         }
118 
GetProjectedValueSystem.Reflection.Metadata.CustomAttribute119         private BlobHandle GetProjectedValue(CustomAttributeValueTreatment treatment)
120         {
121             BlobHandle.VirtualIndex virtualIndex;
122             bool isVersionOrDeprecated;
123             switch (treatment)
124             {
125                 case CustomAttributeValueTreatment.AttributeUsageVersionAttribute:
126                 case CustomAttributeValueTreatment.AttributeUsageDeprecatedAttribute:
127                     virtualIndex = BlobHandle.VirtualIndex.AttributeUsage_AllowMultiple;
128                     isVersionOrDeprecated = true;
129                     break;
130 
131                 case CustomAttributeValueTreatment.AttributeUsageAllowMultiple:
132                     virtualIndex = BlobHandle.VirtualIndex.AttributeUsage_AllowMultiple;
133                     isVersionOrDeprecated = false;
134                     break;
135 
136                 case CustomAttributeValueTreatment.AttributeUsageAllowSingle:
137                     virtualIndex = BlobHandle.VirtualIndex.AttributeUsage_AllowSingle;
138                     isVersionOrDeprecated = false;
139                     break;
140 
141                 default:
142                     Debug.Assert(false);
143                     return default(BlobHandle);
144             }
145 
146             // Raw blob format:
147             //    01 00        - Fixed prolog for CA's
148             //    xx xx xx xx  - The Windows.Foundation.Metadata.AttributeTarget value
149             //    00 00        - Indicates 0 name/value pairs following.
150             var rawBlob = _reader.CustomAttributeTable.GetValue(Handle);
151             var rawBlobReader = _reader.GetBlobReader(rawBlob);
152             if (rawBlobReader.Length != 8)
153             {
154                 return rawBlob;
155             }
156 
157             if (rawBlobReader.ReadInt16() != 1)
158             {
159                 return rawBlob;
160             }
161 
162             AttributeTargets projectedValue = ProjectAttributeTargetValue(rawBlobReader.ReadUInt32());
163             if (isVersionOrDeprecated)
164             {
165                 projectedValue |= AttributeTargets.Constructor | AttributeTargets.Property;
166             }
167 
168             return BlobHandle.FromVirtualIndex(virtualIndex, (ushort)projectedValue);
169         }
170 
ProjectAttributeTargetValueSystem.Reflection.Metadata.CustomAttribute171         private static AttributeTargets ProjectAttributeTargetValue(uint rawValue)
172         {
173             // Windows.Foundation.Metadata.AttributeTargets.All
174             if (rawValue == 0xffffffff)
175             {
176                 return AttributeTargets.All;
177             }
178 
179             AttributeTargets result = 0;
180 
181             if ((rawValue & 0x00000001) != 0) result |= AttributeTargets.Delegate;
182             if ((rawValue & 0x00000002) != 0) result |= AttributeTargets.Enum;
183             if ((rawValue & 0x00000004) != 0) result |= AttributeTargets.Event;
184             if ((rawValue & 0x00000008) != 0) result |= AttributeTargets.Field;
185             if ((rawValue & 0x00000010) != 0) result |= AttributeTargets.Interface;
186             // InterfaceGroup (no equivalent in CLR)
187             if ((rawValue & 0x00000040) != 0) result |= AttributeTargets.Method;
188             if ((rawValue & 0x00000080) != 0) result |= AttributeTargets.Parameter;
189             if ((rawValue & 0x00000100) != 0) result |= AttributeTargets.Property;
190             if ((rawValue & 0x00000200) != 0) result |= AttributeTargets.Class;
191             if ((rawValue & 0x00000400) != 0) result |= AttributeTargets.Struct;
192             // InterfaceImpl (no equivalent in CLR)
193 
194             return result;
195         }
196         #endregion
197     }
198 }
199