| File: System\Reflection\Metadata\TypeSystem\CustomAttribute.cs | Web Access |
| Project: src\runtime\src\libraries\System.Reflection.Metadata\src\System.Reflection.Metadata.csproj (System.Reflection.Metadata) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { public readonly struct CustomAttribute { private readonly MetadataReader _reader; // Workaround: JIT doesn't generate good code for nested structures, so use RowId. private readonly uint _treatmentAndRowId; internal CustomAttribute(MetadataReader reader, uint treatmentAndRowId) { _reader = reader; _treatmentAndRowId = treatmentAndRowId; } private int RowId { get { return (int)(_treatmentAndRowId & TokenTypeIds.RIDMask); } } private CustomAttributeHandle Handle { get { return CustomAttributeHandle.FromRowId(RowId); } } private MethodDefTreatment Treatment { get { return (MethodDefTreatment)(_treatmentAndRowId >> TokenTypeIds.RowIdBitCount); } } /// <summary> /// The constructor (<see cref="MethodDefinitionHandle"/> or <see cref="MemberReferenceHandle"/>) of the custom attribute type. /// </summary> /// <remarks> /// Corresponds to Type field of CustomAttribute table in ECMA-335 Standard. /// </remarks> public EntityHandle Constructor { get { return _reader.CustomAttributeTable.GetConstructor(Handle); } } /// <summary> /// The handle of the metadata entity the attribute is applied to. /// </summary> /// <remarks> /// Corresponds to Parent field of CustomAttribute table in ECMA-335 Standard. /// </remarks> public EntityHandle Parent { get { return _reader.CustomAttributeTable.GetParent(Handle); } } /// <summary> /// The value of the attribute. /// </summary> /// <remarks> /// Corresponds to Value field of CustomAttribute table in ECMA-335 Standard. /// </remarks> public BlobHandle Value { get { if (Treatment == 0) { return _reader.CustomAttributeTable.GetValue(Handle); } return GetProjectedValue(); } } /// <summary> /// Decodes the arguments encoded in the value blob. /// </summary> public CustomAttributeValue<TType> DecodeValue<TType>(ICustomAttributeTypeProvider<TType> provider) { var decoder = new CustomAttributeDecoder<TType>(provider, _reader); return decoder.DecodeValue(Constructor, Value); } #region Projections private BlobHandle GetProjectedValue() { // The usual pattern for accessing custom attributes differs from pattern for accessing e.g. TypeDef row fields. // The value blob is only accessed when the consumer is about to decode it (which is a nontrivial process), // while the Constructor and Parent fields are often accessed when searching for a particular attribute. // // The current WinMD projections only affect the blob and not the Constructor and Parent values. // It is thus more efficient to calculate the treatment here (and make GetValue more expensive) and // avoid calculating the treatment when the CustomAttributeHandle is looked up and CustomAttribute struct // is initialized. CustomAttributeValueTreatment treatment = _reader.CalculateCustomAttributeValueTreatment(Handle); if (treatment == 0) { return _reader.CustomAttributeTable.GetValue(Handle); } return GetProjectedValue(treatment); } private BlobHandle GetProjectedValue(CustomAttributeValueTreatment treatment) { BlobHandle.VirtualIndex virtualIndex; bool isVersionOrDeprecated; switch (treatment) { case CustomAttributeValueTreatment.AttributeUsageVersionAttribute: case CustomAttributeValueTreatment.AttributeUsageDeprecatedAttribute: virtualIndex = BlobHandle.VirtualIndex.AttributeUsage_AllowMultiple; isVersionOrDeprecated = true; break; case CustomAttributeValueTreatment.AttributeUsageAllowMultiple: virtualIndex = BlobHandle.VirtualIndex.AttributeUsage_AllowMultiple; isVersionOrDeprecated = false; break; case CustomAttributeValueTreatment.AttributeUsageAllowSingle: virtualIndex = BlobHandle.VirtualIndex.AttributeUsage_AllowSingle; isVersionOrDeprecated = false; break; default: Debug.Fail($"Unexpected treatment {treatment}"); return default(BlobHandle); } // Raw blob format: // 01 00 - Fixed prolog for CA's // xx xx xx xx - The Windows.Foundation.Metadata.AttributeTarget value // 00 00 - Indicates 0 name/value pairs following. var rawBlob = _reader.CustomAttributeTable.GetValue(Handle); var rawBlobReader = _reader.GetBlobReader(rawBlob); if (rawBlobReader.Length != 8) { return rawBlob; } if (rawBlobReader.ReadInt16() != 1) { return rawBlob; } AttributeTargets projectedValue = ProjectAttributeTargetValue(rawBlobReader.ReadUInt32()); if (isVersionOrDeprecated) { projectedValue |= AttributeTargets.Constructor | AttributeTargets.Property; } return BlobHandle.FromVirtualIndex(virtualIndex, (ushort)projectedValue); } private static AttributeTargets ProjectAttributeTargetValue(uint rawValue) { // Windows.Foundation.Metadata.AttributeTargets.All if (rawValue == 0xffffffff) { return AttributeTargets.All; } AttributeTargets result = 0; if ((rawValue & 0x00000001) != 0) result |= AttributeTargets.Delegate; if ((rawValue & 0x00000002) != 0) result |= AttributeTargets.Enum; if ((rawValue & 0x00000004) != 0) result |= AttributeTargets.Event; if ((rawValue & 0x00000008) != 0) result |= AttributeTargets.Field; if ((rawValue & 0x00000010) != 0) result |= AttributeTargets.Interface; // InterfaceGroup (no equivalent in CLR) if ((rawValue & 0x00000040) != 0) result |= AttributeTargets.Method; if ((rawValue & 0x00000080) != 0) result |= AttributeTargets.Parameter; if ((rawValue & 0x00000100) != 0) result |= AttributeTargets.Property; if ((rawValue & 0x00000200) != 0) result |= AttributeTargets.Class; if ((rawValue & 0x00000400) != 0) result |= AttributeTargets.Struct; // InterfaceImpl (no equivalent in CLR) return result; } #endregion } }