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