| File: Symbols\Attributes\PEAttributeData.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// 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; } }