IteratorMethodToStateMachineRewriter.YieldsInTryAnalysis.cs 5.3 KB
Newer Older
1
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
P
Pilchie 已提交
2 3 4 5 6 7 8

using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Utilities;
using System.Collections.Generic;

namespace Microsoft.CodeAnalysis.CSharp
{
9
    internal partial class IteratorMethodToStateMachineRewriter
P
Pilchie 已提交
10 11
    {
        /// <summary>
12
        /// Analyzes method body for yields in try blocks and labels that they contain.
P
Pilchie 已提交
13
        /// </summary>
14
        private sealed class YieldsInTryAnalysis : LabelCollector
P
Pilchie 已提交
15
        {
16 17
            // 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
P
Pilchie 已提交
18
            //       in the label set of the nearest yielding-try parent  
19
            private Dictionary<BoundTryStatement, HashSet<LabelSymbol>> _labelsInYieldingTrys;
P
Pilchie 已提交
20 21

            // transient accumulators.
22
            private bool _seenYield;
P
Pilchie 已提交
23 24 25

            public YieldsInTryAnalysis(BoundStatement body)
            {
26
                _seenYield = false;
P
Pilchie 已提交
27 28 29 30
                this.Visit(body);
            }

            /// <summary>
31
            /// Returns true if given try or any of its nested try blocks contain yields
P
Pilchie 已提交
32 33 34
            /// </summary>
            public bool ContainsYields(BoundTryStatement statement)
            {
35
                return _labelsInYieldingTrys != null && _labelsInYieldingTrys.ContainsKey(statement);
P
Pilchie 已提交
36 37 38 39 40 41 42
            }

            /// <summary>
            /// Returns true if body contains yield returns within try blocks.
            /// </summary>
            public bool ContainsYieldsInTrys()
            {
43
                return _labelsInYieldingTrys != null;
P
Pilchie 已提交
44 45 46 47 48 49 50 51
            }

            /// <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)
            {
52
                return _labelsInYieldingTrys[statement];
P
Pilchie 已提交
53 54 55 56
            }

            public override BoundNode VisitTryStatement(BoundTryStatement node)
            {
57
                var origSeenYield = _seenYield;
P
Pilchie 已提交
58 59
                var origLabels = this.currentLabels;

60
                // sibling try blocks do not see each other's yields
61
                _seenYield = false;
P
Pilchie 已提交
62 63 64 65
                this.currentLabels = null;

                base.VisitTryStatement(node);

66
                if (_seenYield)
P
Pilchie 已提交
67 68 69
                {
                    // this try yields !

70
                    var yieldingTryLabels = _labelsInYieldingTrys;
P
Pilchie 已提交
71 72
                    if (yieldingTryLabels == null)
                    {
73
                        _labelsInYieldingTrys = yieldingTryLabels = new Dictionary<BoundTryStatement, HashSet<LabelSymbol>>();
P
Pilchie 已提交
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
                    }

                    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);
                    }
                }

94
                _seenYield = _seenYield | origSeenYield;
P
Pilchie 已提交
95 96 97 98 99
                return null;
            }

            public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement node)
            {
100
                _seenYield = true;
P
Pilchie 已提交
101 102 103 104 105 106 107 108 109 110 111 112
                return base.VisitYieldReturnStatement(node);
            }

            public override BoundNode VisitExpressionStatement(BoundExpressionStatement node)
            {
                // expressions cannot contain labels, branches or yields.
                return null;
            }
        }
    }

    /// <summary>
113
    /// Analyzes method body for labels.
P
Pilchie 已提交
114
    /// </summary>
C
CyrusNajmabadi 已提交
115
    internal abstract class LabelCollector : BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
P
Pilchie 已提交
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
    {
        // transient accumulator.
        protected HashSet<LabelSymbol> currentLabels;

        public override BoundNode VisitLabelStatement(BoundLabelStatement node)
        {
            CollectLabel(node.Label);
            return base.VisitLabelStatement(node);
        }

        public override BoundNode VisitSwitchStatement(BoundSwitchStatement node)
        {
            CollectLabel(node.ConstantTargetOpt);
            CollectLabel(node.BreakLabel);
            return base.VisitSwitchStatement(node);
        }

        public override BoundNode VisitSwitchLabel(BoundSwitchLabel node)
        {
            CollectLabel(node.Label);
            return base.VisitSwitchLabel(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);
            }
        }
    }
152
}