Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Analysis/ReachabilityAnalysis.cs

Last change on this file was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 7.5 KB
Line 
1// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19using System;
20using System.Collections.Generic;
21using System.Linq;
22using System.Threading;
23using ICSharpCode.NRefactory.CSharp.Resolver;
24using ICSharpCode.NRefactory.CSharp.TypeSystem;
25using ICSharpCode.NRefactory.Semantics;
26using ICSharpCode.NRefactory.CSharp;
27
28namespace ICSharpCode.NRefactory.CSharp.Analysis
29{
30  /// <summary>
31  /// Statement reachability analysis.
32  /// </summary>
33  public sealed class ReachabilityAnalysis
34  {
35    HashSet<Statement> reachableStatements = new HashSet<Statement>();
36    HashSet<Statement> reachableEndPoints = new HashSet<Statement>();
37    HashSet<ControlFlowNode> visitedNodes = new HashSet<ControlFlowNode>();
38    Stack<ControlFlowNode> stack = new Stack<ControlFlowNode>();
39    RecursiveDetectorVisitor recursiveDetectorVisitor = null;
40   
41    private ReachabilityAnalysis() {}
42   
43    public static ReachabilityAnalysis Create(Statement statement, CSharpAstResolver resolver = null, RecursiveDetectorVisitor recursiveDetectorVisitor = null, CancellationToken cancellationToken = default(CancellationToken))
44    {
45      var cfgBuilder = new ControlFlowGraphBuilder();
46      var cfg = cfgBuilder.BuildControlFlowGraph(statement, resolver, cancellationToken);
47      return Create(cfg, recursiveDetectorVisitor, cancellationToken);
48    }
49   
50    internal static ReachabilityAnalysis Create(Statement statement, Func<AstNode, CancellationToken, ResolveResult> resolver, CSharpTypeResolveContext typeResolveContext, CancellationToken cancellationToken)
51    {
52      var cfgBuilder = new ControlFlowGraphBuilder();
53      var cfg = cfgBuilder.BuildControlFlowGraph(statement, resolver, typeResolveContext, cancellationToken);
54      return Create(cfg, null, cancellationToken);
55    }
56   
57    public static ReachabilityAnalysis Create(IList<ControlFlowNode> controlFlowGraph, RecursiveDetectorVisitor recursiveDetectorVisitor = null, CancellationToken cancellationToken = default(CancellationToken))
58    {
59      if (controlFlowGraph == null)
60        throw new ArgumentNullException("controlFlowGraph");
61      ReachabilityAnalysis ra = new ReachabilityAnalysis();
62      ra.recursiveDetectorVisitor = recursiveDetectorVisitor;
63      // Analysing a null node can result in an empty control flow graph
64      if (controlFlowGraph.Count > 0) {
65        ra.stack.Push(controlFlowGraph[0]);
66        while (ra.stack.Count > 0) {
67          cancellationToken.ThrowIfCancellationRequested();
68          ra.MarkReachable(ra.stack.Pop());
69        }
70      }
71      ra.stack = null;
72      ra.visitedNodes = null;
73      return ra;
74    }
75   
76    void MarkReachable(ControlFlowNode node)
77    {
78      if (node.PreviousStatement != null) {
79        if (node.PreviousStatement is LabelStatement) {
80          reachableStatements.Add(node.PreviousStatement);
81        }
82        reachableEndPoints.Add(node.PreviousStatement);
83      }
84      if (node.NextStatement != null) {
85        reachableStatements.Add(node.NextStatement);
86        if (IsRecursive(node.NextStatement)) {
87          return;
88        }
89      }
90      foreach (var edge in node.Outgoing) {
91        if (visitedNodes.Add(edge.To))
92          stack.Push(edge.To);
93      }
94    }
95
96    bool IsRecursive(Statement statement)
97    {
98      return recursiveDetectorVisitor != null && statement.AcceptVisitor(recursiveDetectorVisitor);
99    }
100   
101    public IEnumerable<Statement> ReachableStatements {
102      get { return reachableStatements; }
103    }
104   
105    public bool IsReachable(Statement statement)
106    {
107      return reachableStatements.Contains(statement);
108    }
109   
110    public bool IsEndpointReachable(Statement statement)
111    {
112      return reachableEndPoints.Contains(statement);
113    }
114
115    public class RecursiveDetectorVisitor : DepthFirstAstVisitor<bool>
116    {
117      public override bool VisitConditionalExpression(ConditionalExpression conditionalExpression)
118      {
119        if (conditionalExpression.Condition.AcceptVisitor(this))
120          return true;
121
122        if (!conditionalExpression.TrueExpression.AcceptVisitor(this))
123          return false;
124
125        return conditionalExpression.FalseExpression.AcceptVisitor(this);
126      }
127
128      public override bool VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression)
129      {
130        if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) {
131          return binaryOperatorExpression.Left.AcceptVisitor(this);
132        }
133        return base.VisitBinaryOperatorExpression(binaryOperatorExpression);
134      }
135
136      public override bool VisitIfElseStatement(IfElseStatement ifElseStatement)
137      {
138        if (ifElseStatement.Condition.AcceptVisitor(this))
139          return true;
140
141        if (!ifElseStatement.TrueStatement.AcceptVisitor(this))
142          return false;
143
144        //No need to worry about null ast nodes, since AcceptVisitor will just
145        //return false in those cases
146        return ifElseStatement.FalseStatement.AcceptVisitor(this);
147      }
148
149      public override bool VisitForeachStatement(ForeachStatement foreachStatement)
150      {
151        //Even if the body is always recursive, the function may stop if the collection
152        // is empty.
153        return foreachStatement.InExpression.AcceptVisitor(this);
154      }
155
156      public override bool VisitForStatement(ForStatement forStatement)
157      {
158        if (forStatement.Initializers.Any(initializer => initializer.AcceptVisitor(this)))
159          return true;
160
161        return forStatement.Condition.AcceptVisitor(this);
162      }
163
164      public override bool VisitSwitchStatement(SwitchStatement switchStatement)
165      {
166        if (switchStatement.Expression.AcceptVisitor(this)) {
167          return true;
168        }
169
170        bool foundDefault = false;
171        foreach (var section in switchStatement.SwitchSections) {
172          foundDefault = foundDefault || section.CaseLabels.Any(label => label.Expression.IsNull);
173          if (!section.AcceptVisitor(this))
174            return false;
175        }
176
177        return foundDefault;
178      }
179
180      public override bool VisitBlockStatement(BlockStatement blockStatement)
181      {
182        //If the block has a recursive statement, then that statement will be visited
183        //individually by the CFG construction algorithm later.
184        return false;
185      }
186
187      protected override bool VisitChildren(AstNode node)
188      {
189        return VisitNodeList(node.Children);
190      }
191
192      bool VisitNodeList(IEnumerable<AstNode> nodes) {
193        return nodes.Any(node => node.AcceptVisitor(this));
194      }
195
196      public override bool VisitQueryExpression(QueryExpression queryExpression)
197      {
198        //We only care about the first from clause because:
199        //in "from x in Method() select x", Method() might be recursive
200        //but in "from x in Bar() from y in Method() select x + y", even if Method() is recursive
201        //Bar might still be empty.
202        var queryFromClause = queryExpression.Clauses.OfType<QueryFromClause>().FirstOrDefault();
203        if (queryFromClause == null)
204          return true;
205        return queryFromClause.AcceptVisitor(this);
206      }
207    }
208  }
209}
Note: See TracBrowser for help on using the repository browser.