|
// 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)
{
Debug.Assert(reader != null);
Debug.Assert(treatmentAndRowId != 0);
_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
}
}
|