Beispiel #1
0
        /// <summary>
        /// Returns a bag of applied custom attributes and data decoded from well-known attributes. Returns null if there are no attributes applied on the symbol.
        /// </summary>
        /// <remarks>
        /// Forces binding and decoding of attributes.
        /// </remarks>
        internal virtual CustomAttributesBag <CSharpAttributeData> GetAttributesBag()
        {
            if (_lazyCustomAttributesBag == null || !_lazyCustomAttributesBag.IsSealed)
            {
                bool lazyAttributesStored = false;

                var sourceMethod = this.ContainingSymbol as SourceOrdinaryMethodSymbol;
                if ((object)sourceMethod == null || (object)sourceMethod.SourcePartialDefinition == null)
                {
                    lazyAttributesStored = LoadAndValidateAttributes(
                        OneOrMany.Create(this.MergedAttributeDeclarationSyntaxLists),
                        ref _lazyCustomAttributesBag,
                        binderOpt: (ContainingSymbol as LocalFunctionSymbol)?.SignatureBinder);
                }
                else
                {
                    var typeParameter = (SourceTypeParameterSymbolBase)sourceMethod.SourcePartialDefinition.TypeParameters[_ordinal];
                    CustomAttributesBag <CSharpAttributeData> attributesBag = typeParameter.GetAttributesBag();

                    lazyAttributesStored = Interlocked.CompareExchange(ref _lazyCustomAttributesBag, attributesBag, null) == null;
                }

                if (lazyAttributesStored)
                {
                    _state.NotePartComplete(CompletionPart.Attributes);
                }
            }

            return(_lazyCustomAttributesBag);
        }
Beispiel #2
0
        internal override CustomAttributesBag <CSharpAttributeData> GetAttributesBag()
        {
            if (lazyCustomAttributesBag == null || !lazyCustomAttributesBag.IsSealed)
            {
                bool lazyAttributesStored = false;

                var sourceMethod = this.ContainingSymbol as SourceMemberMethodSymbol;
                if ((object)sourceMethod == null || (object)sourceMethod.SourcePartialDefinition == null)
                {
                    lazyAttributesStored = LoadAndValidateAttributes(OneOrMany.Create(this.MergedAttributeDeclarationSyntaxLists), ref lazyCustomAttributesBag);
                }
                else
                {
                    var typeParameter = (SourceTypeParameterSymbolBase)sourceMethod.SourcePartialDefinition.TypeParameters[this.ordinal];
                    CustomAttributesBag <CSharpAttributeData> attributesBag = typeParameter.GetAttributesBag();

                    lazyAttributesStored = Interlocked.CompareExchange(ref lazyCustomAttributesBag, attributesBag, null) == null;
                }

                if (lazyAttributesStored)
                {
                    state.NotePartComplete(CompletionPart.Attributes);
                }
            }

            return(lazyCustomAttributesBag);
        }
Beispiel #3
0
        /// <summary>
        /// This method does the following set of operations in the specified order:
        /// (1) GetAttributesToBind: Merge attributes from the given attributesSyntaxLists and filter out attributes by attribute target.
        /// (2) BindAttributeTypes: Bind all the attribute types to enable early decode of certain well-known attributes by type.
        /// (3) EarlyDecodeWellKnownAttributes: Perform early decoding of certain well-known attributes that could be queried by the binder in subsequent steps.
        ///     (NOTE: This step has the side effect of updating the symbol state based on the data extracted from well known attributes).
        /// (4) GetAttributes: Bind the attributes (attribute arguments and constructor) using bound attribute types.
        /// (5) DecodeWellKnownAttributes: Decode and validate bound well known attributes.
        ///     (NOTE: This step has the side effect of updating the symbol state based on the data extracted from well known attributes).
        /// (6) StoreBoundAttributesAndDoPostValidation:
        ///     (a) Store the bound attributes in lazyCustomAttributes in a thread safe manner.
        ///     (b) Perform some additional post attribute validations, such as
        ///         1) Duplicate attributes, attribute usage target validation, etc.
        ///         2) Post validation for attributes dependent on other attributes
        ///         These validations cannot be performed prior to step 6(a) as we might need to
        ///         perform a GetAttributes() call on a symbol which can introduce a cycle in attribute binding.
        ///         We avoid this cycle by performing such validations in PostDecodeWellKnownAttributes after lazyCustomAttributes have been set.
        ///     NOTE: PostDecodeWellKnownAttributes SHOULD NOT change the symbol state.
        /// </summary>
        /// <remarks>
        /// Current design of early decoding well-known attributes doesn't permit decoding attribute arguments/constructor as this can lead to binding cycles.
        /// For well-known attributes used by the binder, where we need the decoded arguments, we must handle them specially in one of the following possible ways:
        ///   (a) Avoid decoding the attribute arguments during binding and delay the corresponding binder tasks to a separate post-pass executed after binding.
        ///   (b) As the cycles can be caused only when we are binding attribute arguments/constructor, special case the corresponding binder tasks based on the current BinderFlags.
        /// </remarks>
        /// <param name="attributesSyntaxLists"></param>
        /// <param name="lazyCustomAttributesBag"></param>
        /// <param name="symbolPart">Specific part of the symbol to which the attributes apply, or <see cref="AttributeLocation.None"/> if the attributes apply to the symbol itself.</param>
        /// <param name="earlyDecodingOnly">Indicates that only early decoding should be performed.  WARNING: the resulting bag will not be sealed.</param>
        /// <param name="binderOpt">Binder to use. If null, <see cref="DeclaringCompilation"/> GetBinderFactory will be used.</param>
        /// <param name="attributeMatchesOpt">If specified, only load attributes that match this predicate, and any diagnostics produced will be dropped.</param>
        /// <returns>Flag indicating whether lazyCustomAttributes were stored on this thread. Caller should check for this flag and perform NotePartComplete if true.</returns>
        internal bool LoadAndValidateAttributes(
            OneOrMany <SyntaxList <AttributeSyntax> > attributesSyntaxLists,
            ref CustomAttributesBag <CSharpAttributeData> lazyCustomAttributesBag,
            AttributeLocation symbolPart = AttributeLocation.None,
            bool earlyDecodingOnly       = false,
            Binder binderOpt             = null,
            Func <AttributeSyntax, bool> attributeMatchesOpt = null,
            Func <Binder, Binder> contextualBinder           = null
            )
        {
            var diagnostics = DiagnosticBag.GetInstance();
            var compilation = this.DeclaringCompilation;

            ImmutableArray <Binder>          binders;
            ImmutableArray <AttributeSyntax> attributesToBind = this.GetAttributesToBind(attributesSyntaxLists, symbolPart, diagnostics, compilation, attributeMatchesOpt, binderOpt, out binders, contextualBinder);

            Debug.Assert(!attributesToBind.IsDefault);

            ImmutableArray <CSharpAttributeData> boundAttributes;
            WellKnownAttributeData wellKnownAttributeData;

            if (attributesToBind.Any())
            {
                Debug.Assert(!binders.IsDefault);
                Debug.Assert(binders.Length == attributesToBind.Length);

                // Initialize the bag so that data decoded from early attributes can be stored onto it.
                if (lazyCustomAttributesBag == null)
                {
                    Interlocked.CompareExchange(ref lazyCustomAttributesBag, new CustomAttributesBag <CSharpAttributeData>(), null);
                }

                // Bind the attribute types and then early decode them.
                int totalAttributesCount  = attributesToBind.Length;
                var attributeTypesBuilder = new NamedTypeSymbol[totalAttributesCount];

                Binder.BindAttributeTypes(binders, attributesToBind, this, attributeTypesBuilder, diagnostics);
                ImmutableArray <NamedTypeSymbol> boundAttributeTypes = attributeTypesBuilder.AsImmutableOrNull();

                this.EarlyDecodeWellKnownAttributeTypes(boundAttributeTypes, attributesToBind);
                this.PostEarlyDecodeWellKnownAttributeTypes();

                // Bind the attribute in two stages - early and normal.
                var attributesBuilder = new CSharpAttributeData[totalAttributesCount];

                // Early bind and decode some well-known attributes.
                EarlyWellKnownAttributeData earlyData = this.EarlyDecodeWellKnownAttributes(binders, boundAttributeTypes, attributesToBind, symbolPart, attributesBuilder);
                Debug.Assert(!attributesBuilder.Contains((attr) => attr != null && attr.HasErrors));

                // Store data decoded from early bound well-known attributes.
                // TODO: what if this succeeds on another thread, not ours?
                lazyCustomAttributesBag.SetEarlyDecodedWellKnownAttributeData(earlyData);

                if (earlyDecodingOnly)
                {
                    diagnostics.Free(); //NOTE: dropped.
                    return(false);
                }

                // Bind attributes.
                Binder.GetAttributes(binders, attributesToBind, boundAttributeTypes, attributesBuilder, diagnostics);
                boundAttributes = attributesBuilder.AsImmutableOrNull();

                // All attributes must be bound by now.
                Debug.Assert(!boundAttributes.Any((attr) => attr == null));

                // Validate attribute usage and Decode remaining well-known attributes.
                wellKnownAttributeData = this.ValidateAttributeUsageAndDecodeWellKnownAttributes(binders, attributesToBind, boundAttributes, diagnostics, symbolPart);

                // Store data decoded from remaining well-known attributes.
                // TODO: what if this succeeds on another thread but not this thread?
                lazyCustomAttributesBag.SetDecodedWellKnownAttributeData(wellKnownAttributeData);
            }
            else if (earlyDecodingOnly)
            {
                diagnostics.Free(); //NOTE: dropped.
                return(false);
            }
            else
            {
                boundAttributes        = ImmutableArray <CSharpAttributeData> .Empty;
                wellKnownAttributeData = null;
                Interlocked.CompareExchange(ref lazyCustomAttributesBag, CustomAttributesBag <CSharpAttributeData> .WithEmptyData(), null);
                this.PostEarlyDecodeWellKnownAttributeTypes();
            }

            this.PostDecodeWellKnownAttributes(boundAttributes, attributesToBind, diagnostics, symbolPart, wellKnownAttributeData);

            // Store attributes into the bag.
            bool lazyAttributesStoredOnThisThread = false;

            if (lazyCustomAttributesBag.SetAttributes(boundAttributes))
            {
                if (attributeMatchesOpt is null)
                {
                    this.RecordPresenceOfBadAttributes(boundAttributes);
                    AddDeclarationDiagnostics(diagnostics);
                }
                lazyAttributesStoredOnThisThread = true;
                if (lazyCustomAttributesBag.IsEmpty)
                {
                    lazyCustomAttributesBag = CustomAttributesBag <CSharpAttributeData> .Empty;
                }
            }

            Debug.Assert(lazyCustomAttributesBag.IsSealed);
            diagnostics.Free();
            return(lazyAttributesStoredOnThisThread);
        }
Beispiel #4
0
        /// <summary>
        /// This method does the following set of operations in the specified order:
        /// (1) GetAttributesToBind: Merge attributes from the given attributesSyntaxLists and filter out attributes by attribute target.
        /// (2) BindAttributeTypes: Bind all the attribute types to enable early decode of certain well-known attributes by type.
        /// (3) EarlyDecodeWellKnownAttributes: Perform early decoding of certain well-known attributes that could be queried by the binder in subsequent steps.
        ///     (NOTE: This step has the side effect of updating the symbol state based on the data extracted from well known attributes).
        /// (4) GetAttributes: Bind the attributes (attribute arguments and constructor) using bound attribute types.
        /// (5) DecodeWellKnownAttributes: Decode and validate bound well known attributes.
        ///     (NOTE: This step has the side effect of updating the symbol state based on the data extracted from well known attributes).
        /// (6) StoreBoundAttributesAndDoPostValidation:
        ///     (a) Store the bound attributes in lazyCustomAttributes in a thread safe manner.
        ///     (b) Perform some additional post attribute validations, such as
        ///         1) Duplicate attributes, attribute usage target validation, etc.
        ///         2) Post validation for attributes dependant on other attributes
        ///         These validations cannot be performed prior to step 6(a) as we might need to
        ///         perform a GetAttributes() call on a symbol which can introduce a cycle in attribute binding.
        ///         We avoid this cycle by performing such validations in PostDecodeWellKnownAttributes after lazyCustomAttributes have been set.
        ///     NOTE: PostDecodeWellKnownAttributes SHOULD NOT change the symbol state.
        /// </summary>
        /// <remarks>
        /// Current design of early decoding well-known attributes doesn't permit decoding attribute arguments/constructor as this can lead to binding cycles.
        /// For well-known attributes used by the binder, where we need the decoded arguments, we must handle them specially in one of the following possible ways:
        ///   (a) Avoid decoding the attribute arguments during binding and delay the corresponding binder tasks to a separate post-pass executed after binding.
        ///   (b) As the cycles can be caused only when we are binding attribute arguments/constructor, special case the corresponding binder tasks based on the current BinderFlags.
        /// </remarks>
        /// <param name="attributesSyntaxLists"></param>
        /// <param name="lazyCustomAttributesBag"></param>
        /// <param name="symbolPart">Specific part of the symbol to which the attributes apply, or <see cref="AttributeLocation.None"/> if the attributes apply to the symbol itself.</param>
        /// <param name="earlyDecodingOnly">Indicates that only early decoding should be performed.  WARNING: the resulting bag will not be sealed.</param>
        /// <returns>Flag indicating whether lazyCustomAttributes were stored on this thread. Caller should check for this flag and perform NotePartComplete if true.</returns>
        internal bool LoadAndValidateAttributes(
            OneOrMany<SyntaxList<AttributeListSyntax>> attributesSyntaxLists,
            ref CustomAttributesBag<CSharpAttributeData> lazyCustomAttributesBag,
            AttributeLocation symbolPart = AttributeLocation.None,
            bool earlyDecodingOnly = false)
        {
            var diagnostics = DiagnosticBag.GetInstance();
            var compilation = this.DeclaringCompilation;

            ImmutableArray<Binder> binders;
            ImmutableArray<AttributeSyntax> attributesToBind = this.GetAttributesToBind(attributesSyntaxLists, symbolPart, diagnostics, compilation, out binders);
            Debug.Assert(!attributesToBind.IsDefault);

            ImmutableArray<CSharpAttributeData> boundAttributes;
            WellKnownAttributeData wellKnownAttributeData;

            if (attributesToBind.Any())
            {
                Debug.Assert(!binders.IsDefault);
                Debug.Assert(binders.Length == attributesToBind.Length);

                // Initialize the bag so that data decoded from early attributes can be stored onto it.
                if (lazyCustomAttributesBag == null)
                {
                    Interlocked.CompareExchange(ref lazyCustomAttributesBag, new CustomAttributesBag<CSharpAttributeData>(), null);
                }

                // Bind the attribute types and then early decode them.
                int totalAttributesCount = attributesToBind.Length;
                var attributeTypesBuilder = new NamedTypeSymbol[totalAttributesCount];

                Binder.BindAttributeTypes(binders, attributesToBind, this, attributeTypesBuilder, diagnostics);
                ImmutableArray<NamedTypeSymbol> boundAttributeTypes = attributeTypesBuilder.AsImmutableOrNull();

                this.EarlyDecodeWellKnownAttributeTypes(boundAttributeTypes, attributesToBind);
                this.PostEarlyDecodeWellKnownAttributeTypes();

                // Bind the attribute in two stages - early and normal.
                var attributesBuilder = new CSharpAttributeData[totalAttributesCount];

                // Early bind and decode some well-known attributes.
                EarlyWellKnownAttributeData earlyData = this.EarlyDecodeWellKnownAttributes(binders, boundAttributeTypes, attributesToBind, symbolPart, attributesBuilder);
                Debug.Assert(!attributesBuilder.Contains((attr) => attr != null && attr.HasErrors));

                // Store data decoded from early bound well-known attributes.
                // TODO: what if this succeeds on another thread, not ours?
                lazyCustomAttributesBag.SetEarlyDecodedWellKnownAttributeData(earlyData);

                if (earlyDecodingOnly)
                {
                    diagnostics.Free(); //NOTE: dropped.
                    return false;
                }

                // Bind attributes.
                Binder.GetAttributes(binders, attributesToBind, boundAttributeTypes, attributesBuilder, diagnostics);
                boundAttributes = attributesBuilder.AsImmutableOrNull();

                // All attributes must be bound by now.
                Debug.Assert(!boundAttributes.Any((attr) => attr == null));

                // Validate attribute usage and Decode remaining well-known attributes.
                wellKnownAttributeData = this.ValidateAttributeUsageAndDecodeWellKnownAttributes(binders, attributesToBind, boundAttributes, diagnostics, symbolPart);

                // Store data decoded from remaining well-known attributes.
                // TODO: what if this succeeds on another thread but not this thread?
                lazyCustomAttributesBag.SetDecodedWellKnownAttributeData(wellKnownAttributeData);
            }
            else if (earlyDecodingOnly)
            {
                diagnostics.Free(); //NOTE: dropped.
                return false;
            }
            else
            {
                boundAttributes = ImmutableArray<CSharpAttributeData>.Empty;
                wellKnownAttributeData = null;
                Interlocked.CompareExchange(ref lazyCustomAttributesBag, CustomAttributesBag<CSharpAttributeData>.WithEmptyData(), null);
                this.PostEarlyDecodeWellKnownAttributeTypes();
            }

            this.PostDecodeWellKnownAttributes(boundAttributes, attributesToBind, diagnostics, symbolPart, wellKnownAttributeData);

            // Store attributes into the bag.
            bool lazyAttributesStoredOnThisThread = false;
            if (lazyCustomAttributesBag.SetAttributes(boundAttributes))
            {
                this.RecordPresenceOfBadAttributes(boundAttributes);
                this.AddDeclarationDiagnostics(diagnostics);
                lazyAttributesStoredOnThisThread = true;
                if (lazyCustomAttributesBag.IsEmpty) lazyCustomAttributesBag = CustomAttributesBag<CSharpAttributeData>.Empty;
            }

            Debug.Assert(lazyCustomAttributesBag.IsSealed);
            diagnostics.Free();
            return lazyAttributesStoredOnThisThread;
        }
Beispiel #5
0
        private CustomAttributesBag<CSharpAttributeData> GetAttributesBag(ref CustomAttributesBag<CSharpAttributeData> lazyCustomAttributesBag, bool forReturnType)
        {
            SourceMethodSymbol copyFrom = this.BoundAttributesSource;

            // prevent infinite recursion:
            Debug.Assert(!ReferenceEquals(copyFrom, this));

            bool bagCreatedOnThisThread;
            if ((object)copyFrom != null)
            {
                var attributesBag = forReturnType ? copyFrom.GetReturnTypeAttributesBag() : copyFrom.GetAttributesBag();
                bagCreatedOnThisThread = Interlocked.CompareExchange(ref lazyCustomAttributesBag, attributesBag, null) == null;
            }
            else if (forReturnType)
            {
                bagCreatedOnThisThread = LoadAndValidateAttributes(this.GetReturnTypeAttributeDeclarations(), ref lazyCustomAttributesBag, symbolPart: AttributeLocation.Return);
            }
            else
            {
                bagCreatedOnThisThread = LoadAndValidateAttributes(this.GetAttributeDeclarations(), ref lazyCustomAttributesBag);
            }

            var part = forReturnType ? CompletionPart.ReturnTypeAttributes : CompletionPart.Attributes;
            state.NotePartComplete(part);
            return lazyCustomAttributesBag;
        }