|
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection.Metadata;
using System.Threading;
using Microsoft.CodeAnalysis.Collections;
namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
{
/// <summary>
/// Represents a PE custom attribute
/// </summary>
internal sealed class PEAttributeData : CSharpAttributeData
{
private readonly MetadataDecoder _decoder;
private readonly CustomAttributeHandle _handle;
private NamedTypeSymbol? _lazyAttributeClass = ErrorTypeSymbol.UnknownResultType; // Indicates uninitialized.
private MethodSymbol? _lazyAttributeConstructor;
private ImmutableArray<TypedConstant> _lazyConstructorArguments;
private ImmutableArray<KeyValuePair<string, TypedConstant>> _lazyNamedArguments;
private ThreeState _lazyHasErrors = ThreeState.Unknown;
internal PEAttributeData(PEModuleSymbol moduleSymbol, CustomAttributeHandle handle)
{
_decoder = new MetadataDecoder(moduleSymbol);
_handle = handle;
}
public override NamedTypeSymbol? AttributeClass
{
get
{
EnsureClassAndConstructorSymbolsAreLoaded();
return _lazyAttributeClass;
}
}
public override MethodSymbol? AttributeConstructor
{
get
{
EnsureClassAndConstructorSymbolsAreLoaded();
return _lazyAttributeConstructor;
}
}
public override SyntaxReference? ApplicationSyntaxReference
{
get { return null; }
}
protected internal override ImmutableArray<TypedConstant> CommonConstructorArguments
{
get
{
EnsureAttributeArgumentsAreLoaded();
return _lazyConstructorArguments;
}
}
protected internal override ImmutableArray<KeyValuePair<string, TypedConstant>> CommonNamedArguments
{
get
{
EnsureAttributeArgumentsAreLoaded();
return _lazyNamedArguments;
}
}
private void EnsureClassAndConstructorSymbolsAreLoaded()
{
#pragma warning disable 0252
if ((object?)_lazyAttributeClass == ErrorTypeSymbol.UnknownResultType)
{
TypeSymbol? attributeClass;
MethodSymbol? attributeConstructor;
if (!_decoder.GetCustomAttribute(_handle, out attributeClass, out attributeConstructor))
{
// TODO: should we create CSErrorTypeSymbol for attribute class??
_lazyHasErrors = ThreeState.True;
}
else if ((object)attributeClass == null || attributeClass.IsErrorType() || (object)attributeConstructor == null)
{
_lazyHasErrors = ThreeState.True;
}
Interlocked.CompareExchange(ref _lazyAttributeConstructor, attributeConstructor, null);
Interlocked.CompareExchange(ref _lazyAttributeClass, (NamedTypeSymbol?)attributeClass, ErrorTypeSymbol.UnknownResultType); // Serves as a flag, so do it last.
}
#pragma warning restore 0252
}
private void EnsureAttributeArgumentsAreLoaded()
{
if (RoslynImmutableInterlocked.VolatileRead(in _lazyConstructorArguments).IsDefault || RoslynImmutableInterlocked.VolatileRead(in _lazyNamedArguments).IsDefault)
{
TypedConstant[]? lazyConstructorArguments = null;
KeyValuePair<string, TypedConstant>[]? lazyNamedArguments = null;
if (!_decoder.GetCustomAttribute(_handle, AttributeConstructor, out lazyConstructorArguments, out lazyNamedArguments))
{
_lazyHasErrors = ThreeState.True;
}
Debug.Assert(lazyConstructorArguments != null && lazyNamedArguments != null);
ImmutableInterlocked.InterlockedInitialize(ref _lazyConstructorArguments,
ImmutableArray.Create<TypedConstant>(lazyConstructorArguments));
ImmutableInterlocked.InterlockedInitialize(ref _lazyNamedArguments,
ImmutableArray.Create<KeyValuePair<string, TypedConstant>>(lazyNamedArguments));
}
}
/// <summary>
/// Matches an attribute by metadata namespace, metadata type name. Does not load the type symbol for
/// the attribute.
/// </summary>
/// <param name="namespaceName"></param>
/// <param name="typeName"></param>
/// <returns>True if the attribute data matches.</returns>
internal override bool IsTargetAttribute(string namespaceName, string typeName)
{
// Matching an attribute by name should not load the attribute class.
return _decoder.IsTargetAttribute(_handle, namespaceName, typeName);
}
/// <summary>
/// Matches an attribute by metadata namespace, metadata type name and metadata signature. Does not load the
/// type symbol for the attribute.
/// </summary>
/// <param name="description">Attribute to match.</param>
/// <returns>
/// An index of the target constructor signature in
/// signatures array, -1 if
/// this is not the target attribute.
/// </returns>
internal override int GetTargetAttributeSignatureIndex(AttributeDescription description)
{
// Matching an attribute by name should not load the attribute class.
return _decoder.GetTargetAttributeSignatureIndex(_handle, description);
}
internal override Location GetAttributeArgumentLocation(int parameterIndex)
{
return new MetadataLocation(_decoder.ModuleSymbol);
}
[MemberNotNullWhen(false, nameof(AttributeClass), nameof(AttributeConstructor))]
internal override bool HasErrors
{
get
{
if (_lazyHasErrors == ThreeState.Unknown)
{
EnsureClassAndConstructorSymbolsAreLoaded();
EnsureAttributeArgumentsAreLoaded();
if (_lazyHasErrors == ThreeState.Unknown)
{
_lazyHasErrors = ThreeState.False;
}
}
return _lazyHasErrors.Value();
}
}
internal override DiagnosticInfo? ErrorInfo
{
get
{
if (HasErrors)
{
switch (AttributeConstructor)
{
case { HasUseSiteError: true } attributeConstructor:
return attributeConstructor.GetUseSiteInfo().DiagnosticInfo;
case { }:
return new CSDiagnosticInfo(ErrorCode.ERR_BogusType, string.Empty);
default:
switch (AttributeClass)
{
case { HasUseSiteError: true } attributeClass:
return attributeClass.GetUseSiteInfo().DiagnosticInfo;
case { } attributeClass:
return new CSDiagnosticInfo(ErrorCode.ERR_MissingPredefinedMember, attributeClass, WellKnownMemberNames.InstanceConstructorName);
default:
return new CSDiagnosticInfo(ErrorCode.ERR_BogusType, string.Empty);
}
}
}
else
{
return null;
}
}
}
internal override bool IsConditionallyOmitted => false;
}
}
|