| File: Symbols\Attributes\CustomAttributesBag.cs | Web Access |
| Project: src\roslyn\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis) |
// 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. #nullable disable using System.Collections.Immutable; using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; using System; namespace Microsoft.CodeAnalysis { /// <summary> /// Represents a bag of custom attributes and the associated decoded well-known attribute data. /// </summary> internal sealed class CustomAttributesBag<T> where T : AttributeData { private ImmutableArray<T> _customAttributes; private WellKnownAttributeData _decodedWellKnownAttributeData; private EarlyWellKnownAttributeData _earlyDecodedWellKnownAttributeData; private int _state; /// <summary> /// Instance representing sealed custom attribute bag with no attributes. /// </summary> public static readonly CustomAttributesBag<T> Empty = new CustomAttributesBag<T>(CustomAttributeBagCompletionPart.All, ImmutableArray<T>.Empty); private CustomAttributesBag(CustomAttributeBagCompletionPart part, ImmutableArray<T> customAttributes) { _customAttributes = customAttributes; this.NotePartComplete(part); } public CustomAttributesBag() : this(CustomAttributeBagCompletionPart.None, default(ImmutableArray<T>)) { } /// <summary> /// Returns a non-sealed custom attribute bag with null initialized <see cref="_earlyDecodedWellKnownAttributeData"/>, null initialized <see cref="_decodedWellKnownAttributeData"/> and uninitialized <see cref="_customAttributes"/>. /// </summary> public static CustomAttributesBag<T> WithEmptyData() { return new CustomAttributesBag<T>(CustomAttributeBagCompletionPart.EarlyDecodedWellKnownAttributeData | CustomAttributeBagCompletionPart.DecodedWellKnownAttributeData, default(ImmutableArray<T>)); } public bool IsEmpty { get { return this.IsSealed && _customAttributes.IsEmpty && _decodedWellKnownAttributeData == null && _earlyDecodedWellKnownAttributeData == null; } } /// <summary> /// Sets the early decoded well-known attribute data on the bag in a thread safe manner. /// Stored early decoded data is immutable and cannot be updated further. /// </summary> /// <returns>Returns true if early decoded data were stored into the bag on this thread.</returns> public bool SetEarlyDecodedWellKnownAttributeData(EarlyWellKnownAttributeData data) { WellKnownAttributeData.Seal(data); // Early decode must complete before full decode Debug.Assert(!IsPartComplete(CustomAttributeBagCompletionPart.DecodedWellKnownAttributeData) || IsPartComplete(CustomAttributeBagCompletionPart.EarlyDecodedWellKnownAttributeData)); var setOnOurThread = Interlocked.CompareExchange(ref _earlyDecodedWellKnownAttributeData, data, null) == null; NotePartComplete(CustomAttributeBagCompletionPart.EarlyDecodedWellKnownAttributeData); return setOnOurThread; } /// <summary> /// Sets the decoded well-known attribute data (except the early data) on the bag in a thread safe manner. /// Stored decoded data is immutable and cannot be updated further. /// </summary> /// <returns>Returns true if decoded data were stored into the bag on this thread.</returns> public bool SetDecodedWellKnownAttributeData(WellKnownAttributeData data) { WellKnownAttributeData.Seal(data); // Early decode must complete before full decode Debug.Assert(IsPartComplete(CustomAttributeBagCompletionPart.EarlyDecodedWellKnownAttributeData)); var setOnOurThread = Interlocked.CompareExchange(ref _decodedWellKnownAttributeData, data, null) == null; NotePartComplete(CustomAttributeBagCompletionPart.DecodedWellKnownAttributeData); return setOnOurThread; } /// <summary> /// Sets the bound attributes on the bag in a thread safe manner. /// If store succeeds, it seals the bag and makes the bag immutable. /// </summary> /// <returns>Returns true if bound attributes were stored into the bag on this thread.</returns> public bool SetAttributes(ImmutableArray<T> newCustomAttributes) { Debug.Assert(!newCustomAttributes.IsDefault); var setOnOurThread = ImmutableInterlocked.InterlockedCompareExchange(ref _customAttributes, newCustomAttributes, default(ImmutableArray<T>)) == default(ImmutableArray<T>); NotePartComplete(CustomAttributeBagCompletionPart.Attributes); return setOnOurThread; } /// <summary> /// Gets the stored bound attributes in the bag. /// </summary> /// <remarks>This property can only be accessed on a sealed bag.</remarks> public ImmutableArray<T> Attributes { get { Debug.Assert(IsPartComplete(CustomAttributeBagCompletionPart.Attributes)); Debug.Assert(!_customAttributes.IsDefault); return _customAttributes; } } /// <summary> /// Gets the decoded well-known attribute data (except the early data) in the bag. /// </summary> /// <remarks>This property can only be accessed on the bag after <see cref="SetDecodedWellKnownAttributeData"/> has been invoked.</remarks> public WellKnownAttributeData DecodedWellKnownAttributeData { get { Debug.Assert(IsPartComplete(CustomAttributeBagCompletionPart.DecodedWellKnownAttributeData)); return _decodedWellKnownAttributeData; } } /// <summary> /// Gets the early decoded well-known attribute data in the bag. /// </summary> /// <remarks>This property can only be accessed on the bag after <see cref="SetEarlyDecodedWellKnownAttributeData"/> has been invoked.</remarks> public EarlyWellKnownAttributeData EarlyDecodedWellKnownAttributeData { get { Debug.Assert(IsPartComplete(CustomAttributeBagCompletionPart.EarlyDecodedWellKnownAttributeData)); return _earlyDecodedWellKnownAttributeData; } } private CustomAttributeBagCompletionPart State { get { return (CustomAttributeBagCompletionPart)_state; } set { _state = (int)value; } } private void NotePartComplete(CustomAttributeBagCompletionPart part) { ThreadSafeFlagOperations.Set(ref _state, (int)(this.State | part)); } internal bool IsPartComplete(CustomAttributeBagCompletionPart part) { return (this.State & part) == part; } internal bool IsSealed { get { return IsPartComplete(CustomAttributeBagCompletionPart.All); } } /// <summary> /// Return whether early decoded attribute data has been computed and stored on the bag and it is safe to access <see cref="EarlyDecodedWellKnownAttributeData"/> from this bag. /// Return value of true doesn't guarantee that bound attributes or remaining decoded attribute data has also been initialized. /// </summary> internal bool IsEarlyDecodedWellKnownAttributeDataComputed { get { return IsPartComplete(CustomAttributeBagCompletionPart.EarlyDecodedWellKnownAttributeData); } } /// <summary> /// Return whether all decoded attribute data has been computed and stored on the bag and it is safe to access <see cref="DecodedWellKnownAttributeData"/> from this bag. /// Return value of true doesn't guarantee that bound attributes have also been initialized. /// </summary> internal bool IsDecodedWellKnownAttributeDataComputed { get { return IsPartComplete(CustomAttributeBagCompletionPart.DecodedWellKnownAttributeData); } } /// <summary> /// Enum representing the current state of attribute binding/decoding for a corresponding CustomAttributeBag. /// </summary> [Flags] internal enum CustomAttributeBagCompletionPart : byte { /// <summary> /// Bag has been created, but no decoded data or attributes have been stored. /// CustomAttributeBag is in this state during early decoding phase. /// </summary> None = 0, /// <summary> /// Early decoded attribute data has been computed and stored on the bag, but bound attributes or remaining decoded attribute data is not stored. /// Only <see cref="EarlyDecodedWellKnownAttributeData"/> can be accessed from this bag. /// </summary> EarlyDecodedWellKnownAttributeData = 1 << 0, /// <summary> /// All decoded attribute data has been computed and stored on the bag, but bound attributes are not yet stored. /// Both <see cref="EarlyDecodedWellKnownAttributeData"/> and <see cref="DecodedWellKnownAttributeData"/> can be accessed from this bag. /// </summary> DecodedWellKnownAttributeData = 1 << 1, /// <summary> /// Bound attributes have been computed and stored on this bag. /// </summary> Attributes = 1 << 2, /// <summary> /// CustomAttributeBag is completely initialized and immutable. /// </summary> All = EarlyDecodedWellKnownAttributeData | DecodedWellKnownAttributeData | Attributes, } } }