File: Lowering\IteratorRewriter\IteratorMethodToStateMachineRewriter.YieldsInTryAnalysis.cs
Web Access
Project: src\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.
 
#nullable disable
 
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Utilities;
using System.Collections.Generic;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    internal partial class IteratorMethodToStateMachineRewriter
    {
        /// <summary>
        /// Analyzes method body for yields in try blocks and labels that they contain.
        /// </summary>
        private sealed class YieldsInTryAnalysis : LabelCollector
        {
            // all try blocks with yields in them and complete set of labels inside those try blocks
            // NOTE: non-yielding try blocks are transparently ignored - i.e. their labels are included
            //       in the label set of the nearest yielding-try parent  
            private Dictionary<BoundTryStatement, HashSet<LabelSymbol>> _labelsInYieldingTrys;
 
            // transient accumulators.
            private bool _seenYield;
 
            public YieldsInTryAnalysis(BoundStatement body)
            {
                _seenYield = false;
                this.Visit(body);
            }
 
            /// <summary>
            /// Returns true if given try or any of its nested try blocks contain yields
            /// </summary>
            public bool ContainsYields(BoundTryStatement statement)
            {
                return _labelsInYieldingTrys != null && _labelsInYieldingTrys.ContainsKey(statement);
            }
 
            /// <summary>
            /// Returns true if body contains yield returns within try blocks.
            /// </summary>
            public bool ContainsYieldsInTrys()
            {
                return _labelsInYieldingTrys != null;
            }
 
            /// <summary>
            /// Labels reachable from within this frame without invoking its finally. 
            /// null if there are none such labels.
            /// </summary>
            internal HashSet<LabelSymbol> Labels(BoundTryStatement statement)
            {
                return _labelsInYieldingTrys[statement];
            }
 
            public override BoundNode VisitTryStatement(BoundTryStatement node)
            {
                var origSeenYield = _seenYield;
                var origLabels = this.currentLabels;
 
                // sibling try blocks do not see each other's yields
                _seenYield = false;
                this.currentLabels = null;
 
                base.VisitTryStatement(node);
 
                if (_seenYield)
                {
                    // this try yields !
 
                    var yieldingTryLabels = _labelsInYieldingTrys;
                    if (yieldingTryLabels == null)
                    {
                        _labelsInYieldingTrys = yieldingTryLabels = new Dictionary<BoundTryStatement, HashSet<LabelSymbol>>();
                    }
 
                    yieldingTryLabels.Add(node, currentLabels);
                    currentLabels = origLabels;
                }
                else
                {
                    // this is a boring non-yielding try
 
                    // currentLabels = currentLabels U origLabels ;
                    if (currentLabels == null)
                    {
                        currentLabels = origLabels;
                    }
                    else if (origLabels != null)
                    {
                        currentLabels.UnionWith(origLabels);
                    }
                }
 
                _seenYield = _seenYield | origSeenYield;
                return null;
            }
 
            public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement node)
            {
                _seenYield = true;
                return base.VisitYieldReturnStatement(node);
            }
 
            public override BoundNode VisitExpressionStatement(BoundExpressionStatement node)
            {
                // expressions cannot contain labels, branches or yields.
                return null;
            }
        }
    }
 
    /// <summary>
    /// Analyzes method body for labels.
    /// </summary>
    internal abstract class LabelCollector : BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
    {
        // transient accumulator.
        protected HashSet<LabelSymbol> currentLabels;
 
        public override BoundNode VisitLabelStatement(BoundLabelStatement node)
        {
            CollectLabel(node.Label);
            return base.VisitLabelStatement(node);
        }
 
        private void CollectLabel(LabelSymbol label)
        {
            if ((object)label != null)
            {
                var currentLabels = this.currentLabels;
                if (currentLabels == null)
                {
                    this.currentLabels = currentLabels = new HashSet<LabelSymbol>();
                }
                currentLabels.Add(label);
            }
        }
    }
}