Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Analysis/ControlFlow.cs @ 17401

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

#2077: created branch and added first version

File size: 30.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.Diagnostics;
22using System.Linq;
23using System.Threading;
24using ICSharpCode.NRefactory.CSharp.Resolver;
25using ICSharpCode.NRefactory.CSharp.TypeSystem;
26using ICSharpCode.NRefactory.Semantics;
27using ICSharpCode.NRefactory.TypeSystem;
28using ICSharpCode.NRefactory.TypeSystem.Implementation;
29using ICSharpCode.NRefactory.Utils;
30
31namespace ICSharpCode.NRefactory.CSharp.Analysis
32{
33  /// <summary>
34  /// Represents a node in the control flow graph of a C# method.
35  /// </summary>
36  public class ControlFlowNode
37  {
38    public readonly Statement PreviousStatement;
39    public readonly Statement NextStatement;
40   
41    public readonly ControlFlowNodeType Type;
42   
43    public readonly List<ControlFlowEdge> Outgoing = new List<ControlFlowEdge>();
44    public readonly List<ControlFlowEdge> Incoming = new List<ControlFlowEdge>();
45   
46    public ControlFlowNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type)
47    {
48      if (previousStatement == null && nextStatement == null)
49        throw new ArgumentException("previousStatement and nextStatement must not be both null");
50      this.PreviousStatement = previousStatement;
51      this.NextStatement = nextStatement;
52      this.Type = type;
53    }
54  }
55 
56  public enum ControlFlowNodeType
57  {
58    /// <summary>
59    /// Unknown node type
60    /// </summary>
61    None,
62    /// <summary>
63    /// Node in front of a statement
64    /// </summary>
65    StartNode,
66    /// <summary>
67    /// Node between two statements
68    /// </summary>
69    BetweenStatements,
70    /// <summary>
71    /// Node at the end of a statement list
72    /// </summary>
73    EndNode,
74    /// <summary>
75    /// Node representing the position before evaluating the condition of a loop.
76    /// </summary>
77    LoopCondition
78  }
79 
80  public class ControlFlowEdge
81  {
82    public readonly ControlFlowNode From;
83    public readonly ControlFlowNode To;
84    public readonly ControlFlowEdgeType Type;
85   
86    List<TryCatchStatement> jumpOutOfTryFinally;
87   
88    public ControlFlowEdge(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type)
89    {
90      if (from == null)
91        throw new ArgumentNullException("from");
92      if (to == null)
93        throw new ArgumentNullException("to");
94      this.From = from;
95      this.To = to;
96      this.Type = type;
97    }
98   
99    internal void AddJumpOutOfTryFinally(TryCatchStatement tryFinally)
100    {
101      if (jumpOutOfTryFinally == null)
102        jumpOutOfTryFinally = new List<TryCatchStatement>();
103      jumpOutOfTryFinally.Add(tryFinally);
104    }
105   
106    /// <summary>
107    /// Gets whether this control flow edge is leaving any try-finally statements.
108    /// </summary>
109    public bool IsLeavingTryFinally {
110      get { return jumpOutOfTryFinally != null; }
111    }
112   
113    /// <summary>
114    /// Gets the try-finally statements that this control flow edge is leaving.
115    /// </summary>
116    public IEnumerable<TryCatchStatement> TryFinallyStatements {
117      get { return jumpOutOfTryFinally ?? Enumerable.Empty<TryCatchStatement>(); }
118    }
119  }
120 
121  public enum ControlFlowEdgeType
122  {
123    /// <summary>
124    /// Regular control flow.
125    /// </summary>
126    Normal,
127    /// <summary>
128    /// Conditional control flow (edge taken if condition is true)
129    /// </summary>
130    ConditionTrue,
131    /// <summary>
132    /// Conditional control flow (edge taken if condition is false)
133    /// </summary>
134    ConditionFalse,
135    /// <summary>
136    /// A jump statement (goto, goto case, break or continue)
137    /// </summary>
138    Jump
139  }
140 
141  /// <summary>
142  /// Constructs the control flow graph for C# statements.
143  /// </summary>
144  public class ControlFlowGraphBuilder
145  {
146    // Written according to the reachability rules in the C# spec (§8.1 End points and reachability)
147   
148    protected virtual ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type)
149    {
150      cancellationToken.ThrowIfCancellationRequested();
151      return new ControlFlowNode(previousStatement, nextStatement, type);
152    }
153   
154    protected virtual ControlFlowEdge CreateEdge(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type)
155    {
156      cancellationToken.ThrowIfCancellationRequested();
157      return new ControlFlowEdge(from, to, type);
158    }
159   
160    Statement rootStatement;
161    CSharpTypeResolveContext typeResolveContext;
162    Func<AstNode, CancellationToken, ResolveResult> resolver;
163    List<ControlFlowNode> nodes;
164    Dictionary<string, ControlFlowNode> labels;
165    List<ControlFlowNode> gotoStatements;
166    CancellationToken cancellationToken;
167   
168    public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, CancellationToken cancellationToken = default(CancellationToken))
169    {
170      if (statement == null)
171        throw new ArgumentNullException("statement");
172      CSharpResolver r = new CSharpResolver(MinimalCorlib.Instance.CreateCompilation());
173      return BuildControlFlowGraph(statement, new CSharpAstResolver(r, statement), cancellationToken);
174    }
175   
176    public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, CSharpAstResolver resolver, CancellationToken cancellationToken = default(CancellationToken))
177    {
178      if (statement == null)
179        throw new ArgumentNullException("statement");
180      if (resolver == null)
181        throw new ArgumentNullException("resolver");
182      return BuildControlFlowGraph(statement, resolver.Resolve, resolver.TypeResolveContext, cancellationToken);
183    }
184   
185    internal IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, Func<AstNode, CancellationToken, ResolveResult> resolver, CSharpTypeResolveContext typeResolveContext, CancellationToken cancellationToken)
186    {
187      NodeCreationVisitor nodeCreationVisitor = new NodeCreationVisitor();
188      nodeCreationVisitor.builder = this;
189      try {
190        this.nodes = new List<ControlFlowNode>();
191        this.labels = new Dictionary<string, ControlFlowNode>();
192        this.gotoStatements = new List<ControlFlowNode>();
193        this.rootStatement = statement;
194        this.resolver = resolver;
195        this.typeResolveContext = typeResolveContext;
196        this.cancellationToken = cancellationToken;
197       
198        ControlFlowNode entryPoint = CreateStartNode(statement);
199        statement.AcceptVisitor(nodeCreationVisitor, entryPoint);
200       
201        // Resolve goto statements:
202        foreach (ControlFlowNode gotoStmt in gotoStatements) {
203          string label = ((GotoStatement)gotoStmt.NextStatement).Label;
204          ControlFlowNode labelNode;
205          if (labels.TryGetValue(label, out labelNode))
206            nodeCreationVisitor.Connect(gotoStmt, labelNode, ControlFlowEdgeType.Jump);
207        }
208       
209        AnnotateLeaveEdgesWithTryFinallyBlocks();
210       
211        return nodes;
212      } finally {
213        this.nodes = null;
214        this.labels = null;
215        this.gotoStatements = null;
216        this.rootStatement = null;
217        this.resolver = null;
218        this.typeResolveContext = null;
219        this.cancellationToken = CancellationToken.None;
220      }
221    }
222   
223    void AnnotateLeaveEdgesWithTryFinallyBlocks()
224    {
225      foreach (ControlFlowEdge edge in nodes.SelectMany(n => n.Outgoing)) {
226        if (edge.Type != ControlFlowEdgeType.Jump) {
227          // Only jumps are potential candidates for leaving try-finally blocks.
228          // Note that the regular edges leaving try or catch blocks are already annotated by the visitor.
229          continue;
230        }
231        Statement gotoStatement = edge.From.NextStatement;
232        Debug.Assert(gotoStatement is GotoStatement || gotoStatement is GotoDefaultStatement || gotoStatement is GotoCaseStatement || gotoStatement is BreakStatement || gotoStatement is ContinueStatement);
233        Statement targetStatement = edge.To.PreviousStatement ?? edge.To.NextStatement;
234        if (gotoStatement.Parent == targetStatement.Parent)
235          continue;
236        HashSet<TryCatchStatement> targetParentTryCatch = new HashSet<TryCatchStatement>(targetStatement.Ancestors.OfType<TryCatchStatement>());
237        for (AstNode node = gotoStatement.Parent; node != null; node = node.Parent) {
238          TryCatchStatement leftTryCatch = node as TryCatchStatement;
239          if (leftTryCatch != null) {
240            if (targetParentTryCatch.Contains(leftTryCatch))
241              break;
242            if (!leftTryCatch.FinallyBlock.IsNull)
243              edge.AddJumpOutOfTryFinally(leftTryCatch);
244          }
245        }
246      }
247    }
248   
249    #region Create*Node
250    ControlFlowNode CreateStartNode(Statement statement)
251    {
252      if (statement.IsNull)
253        return null;
254      ControlFlowNode node = CreateNode(null, statement, ControlFlowNodeType.StartNode);
255      nodes.Add(node);
256      return node;
257    }
258   
259    ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type, bool addToNodeList = true)
260    {
261      ControlFlowNode node = CreateNode(null, statement, type);
262      if (addToNodeList)
263        nodes.Add(node);
264      return node;
265    }
266   
267    ControlFlowNode CreateEndNode(Statement statement, bool addToNodeList = true)
268    {
269      Statement nextStatement;
270      if (statement == rootStatement) {
271        nextStatement = null;
272      } else {
273        // Find the next statement in the same role:
274        AstNode next = statement;
275        do {
276          next = next.NextSibling;
277        } while (next != null && next.Role != statement.Role);
278        nextStatement = next as Statement;
279      }
280      ControlFlowNodeType type = nextStatement != null ? ControlFlowNodeType.BetweenStatements : ControlFlowNodeType.EndNode;
281      ControlFlowNode node = CreateNode(statement, nextStatement, type);
282      if (addToNodeList)
283        nodes.Add(node);
284      return node;
285    }
286    #endregion
287   
288    #region Constant evaluation
289    /// <summary>
290    /// Gets/Sets whether to handle only primitive expressions as constants (no complex expressions like "a + b").
291    /// </summary>
292    public bool EvaluateOnlyPrimitiveConstants { get; set; }
293   
294    /// <summary>
295    /// Evaluates an expression.
296    /// </summary>
297    /// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
298    ResolveResult EvaluateConstant(Expression expr)
299    {
300      if (expr.IsNull)
301        return null;
302      if (EvaluateOnlyPrimitiveConstants) {
303        if (!(expr is PrimitiveExpression || expr is NullReferenceExpression))
304          return null;
305      }
306      return resolver(expr, cancellationToken);
307    }
308   
309    /// <summary>
310    /// Evaluates an expression.
311    /// </summary>
312    /// <returns>The value of the constant boolean expression; or null if the value is not a constant boolean expression.</returns>
313    bool? EvaluateCondition(Expression expr)
314    {
315      ResolveResult rr = EvaluateConstant(expr);
316      if (rr != null && rr.IsCompileTimeConstant)
317        return rr.ConstantValue as bool?;
318      else
319        return null;
320    }
321   
322    bool AreEqualConstants(ResolveResult c1, ResolveResult c2)
323    {
324      if (c1 == null || c2 == null || !c1.IsCompileTimeConstant || !c2.IsCompileTimeConstant)
325        return false;
326      CSharpResolver r = new CSharpResolver(typeResolveContext);
327      ResolveResult c = r.ResolveBinaryOperator(BinaryOperatorType.Equality, c1, c2);
328      return c.IsCompileTimeConstant && (c.ConstantValue as bool?) == true;
329    }
330    #endregion
331   
332    sealed class NodeCreationVisitor : DepthFirstAstVisitor<ControlFlowNode, ControlFlowNode>
333    {
334      // 'data' parameter: input control flow node (start of statement being visited)
335      // Return value: result control flow node (end of statement being visited)
336     
337      internal ControlFlowGraphBuilder builder;
338      Stack<ControlFlowNode> breakTargets = new Stack<ControlFlowNode>();
339      Stack<ControlFlowNode> continueTargets = new Stack<ControlFlowNode>();
340      List<ControlFlowNode> gotoCaseOrDefault = new List<ControlFlowNode>();
341     
342      internal ControlFlowEdge Connect(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type = ControlFlowEdgeType.Normal)
343      {
344        if (from == null || to == null)
345          return null;
346        ControlFlowEdge edge = builder.CreateEdge(from, to, type);
347        from.Outgoing.Add(edge);
348        to.Incoming.Add(edge);
349        return edge;
350      }
351     
352      /// <summary>
353      /// Creates an end node for <c>stmt</c> and connects <c>from</c> with the new node.
354      /// </summary>
355      ControlFlowNode CreateConnectedEndNode(Statement stmt, ControlFlowNode from)
356      {
357        ControlFlowNode newNode = builder.CreateEndNode(stmt);
358        Connect(from, newNode);
359        return newNode;
360      }
361     
362      protected override ControlFlowNode VisitChildren(AstNode node, ControlFlowNode data)
363      {
364        // We have overrides for all possible statements and should visit statements only.
365        throw new NotSupportedException();
366      }
367     
368      public override ControlFlowNode VisitBlockStatement(BlockStatement blockStatement, ControlFlowNode data)
369      {
370        // C# 4.0 spec: §8.2 Blocks
371        ControlFlowNode childNode = HandleStatementList(blockStatement.Statements, data);
372        return CreateConnectedEndNode(blockStatement, childNode);
373      }
374     
375      ControlFlowNode HandleStatementList(AstNodeCollection<Statement> statements, ControlFlowNode source)
376      {
377        ControlFlowNode childNode = null;
378        foreach (Statement stmt in statements) {
379          if (childNode == null) {
380            childNode = builder.CreateStartNode(stmt);
381            if (source != null)
382              Connect(source, childNode);
383          }
384          Debug.Assert(childNode.NextStatement == stmt);
385          childNode = stmt.AcceptVisitor(this, childNode);
386          Debug.Assert(childNode.PreviousStatement == stmt);
387        }
388        return childNode ?? source;
389      }
390     
391      public override ControlFlowNode VisitEmptyStatement(EmptyStatement emptyStatement, ControlFlowNode data)
392      {
393        return CreateConnectedEndNode(emptyStatement, data);
394      }
395     
396      public override ControlFlowNode VisitLabelStatement(LabelStatement labelStatement, ControlFlowNode data)
397      {
398        ControlFlowNode end = CreateConnectedEndNode(labelStatement, data);
399        builder.labels[labelStatement.Label] = end;
400        return end;
401      }
402     
403      public override ControlFlowNode VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, ControlFlowNode data)
404      {
405        return CreateConnectedEndNode(variableDeclarationStatement, data);
406      }
407     
408      public override ControlFlowNode VisitExpressionStatement(ExpressionStatement expressionStatement, ControlFlowNode data)
409      {
410        return CreateConnectedEndNode(expressionStatement, data);
411      }
412     
413      public override ControlFlowNode VisitIfElseStatement(IfElseStatement ifElseStatement, ControlFlowNode data)
414      {
415        bool? cond = builder.EvaluateCondition(ifElseStatement.Condition);
416       
417        ControlFlowNode trueBegin = builder.CreateStartNode(ifElseStatement.TrueStatement);
418        if (cond != false)
419          Connect(data, trueBegin, ControlFlowEdgeType.ConditionTrue);
420        ControlFlowNode trueEnd = ifElseStatement.TrueStatement.AcceptVisitor(this, trueBegin);
421       
422        ControlFlowNode falseBegin = builder.CreateStartNode(ifElseStatement.FalseStatement);
423        if (cond != true)
424          Connect(data, falseBegin, ControlFlowEdgeType.ConditionFalse);
425        ControlFlowNode falseEnd = ifElseStatement.FalseStatement.AcceptVisitor(this, falseBegin);
426        // (if no else statement exists, both falseBegin and falseEnd will be null)
427       
428        ControlFlowNode end = builder.CreateEndNode(ifElseStatement);
429        Connect(trueEnd, end);
430        if (falseEnd != null) {
431          Connect(falseEnd, end);
432        } else if (cond != true) {
433          Connect(data, end, ControlFlowEdgeType.ConditionFalse);
434        }
435        return end;
436      }
437     
438      public override ControlFlowNode VisitSwitchStatement(SwitchStatement switchStatement, ControlFlowNode data)
439      {
440        // First, figure out which switch section will get called (if the expression is constant):
441        ResolveResult constant = builder.EvaluateConstant(switchStatement.Expression);
442        SwitchSection defaultSection = null;
443        SwitchSection sectionMatchedByConstant = null;
444        foreach (SwitchSection section in switchStatement.SwitchSections) {
445          foreach (CaseLabel label in section.CaseLabels) {
446            if (label.Expression.IsNull) {
447              defaultSection = section;
448            } else if (constant != null && constant.IsCompileTimeConstant) {
449              ResolveResult labelConstant = builder.EvaluateConstant(label.Expression);
450              if (builder.AreEqualConstants(constant, labelConstant))
451                sectionMatchedByConstant = section;
452            }
453          }
454        }
455        if (constant != null && constant.IsCompileTimeConstant && sectionMatchedByConstant == null)
456          sectionMatchedByConstant = defaultSection;
457       
458        int gotoCaseOrDefaultInOuterScope = gotoCaseOrDefault.Count;
459        List<ControlFlowNode> sectionStartNodes = new List<ControlFlowNode>();
460       
461        ControlFlowNode end = builder.CreateEndNode(switchStatement, addToNodeList: false);
462        breakTargets.Push(end);
463        foreach (SwitchSection section in switchStatement.SwitchSections) {
464          int sectionStartNodeID = builder.nodes.Count;
465          if (constant == null || !constant.IsCompileTimeConstant || section == sectionMatchedByConstant) {
466            HandleStatementList(section.Statements, data);
467          } else {
468            // This section is unreachable: pass null to HandleStatementList.
469            HandleStatementList(section.Statements, null);
470          }
471          // Don't bother connecting the ends of the sections: the 'break' statement takes care of that.
472         
473          // Store the section start node for 'goto case' statements.
474          sectionStartNodes.Add(sectionStartNodeID < builder.nodes.Count ? builder.nodes[sectionStartNodeID] : null);
475        }
476        breakTargets.Pop();
477        if (defaultSection == null && sectionMatchedByConstant == null) {
478          Connect(data, end);
479        }
480       
481        if (gotoCaseOrDefault.Count > gotoCaseOrDefaultInOuterScope) {
482          // Resolve 'goto case' statements:
483          for (int i = gotoCaseOrDefaultInOuterScope; i < gotoCaseOrDefault.Count; i++) {
484            ControlFlowNode gotoCaseNode = gotoCaseOrDefault[i];
485            GotoCaseStatement gotoCaseStatement = gotoCaseNode.NextStatement as GotoCaseStatement;
486            ResolveResult gotoCaseConstant = null;
487            if (gotoCaseStatement != null) {
488              gotoCaseConstant = builder.EvaluateConstant(gotoCaseStatement.LabelExpression);
489            }
490            int targetSectionIndex = -1;
491            int currentSectionIndex = 0;
492            foreach (SwitchSection section in switchStatement.SwitchSections) {
493              foreach (CaseLabel label in section.CaseLabels) {
494                if (gotoCaseStatement != null) {
495                  // goto case
496                  if (!label.Expression.IsNull) {
497                    ResolveResult labelConstant = builder.EvaluateConstant(label.Expression);
498                    if (builder.AreEqualConstants(gotoCaseConstant, labelConstant))
499                      targetSectionIndex = currentSectionIndex;
500                  }
501                } else {
502                  // goto default
503                  if (label.Expression.IsNull)
504                    targetSectionIndex = currentSectionIndex;
505                }
506              }
507              currentSectionIndex++;
508            }
509            if (targetSectionIndex >= 0 && sectionStartNodes[targetSectionIndex] != null)
510              Connect(gotoCaseNode, sectionStartNodes[targetSectionIndex], ControlFlowEdgeType.Jump);
511            else
512              Connect(gotoCaseNode, end, ControlFlowEdgeType.Jump);
513          }
514          gotoCaseOrDefault.RemoveRange(gotoCaseOrDefaultInOuterScope, gotoCaseOrDefault.Count - gotoCaseOrDefaultInOuterScope);
515        }
516       
517        builder.nodes.Add(end);
518        return end;
519      }
520     
521      public override ControlFlowNode VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement, ControlFlowNode data)
522      {
523        gotoCaseOrDefault.Add(data);
524        return builder.CreateEndNode(gotoCaseStatement);
525      }
526     
527      public override ControlFlowNode VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement, ControlFlowNode data)
528      {
529        gotoCaseOrDefault.Add(data);
530        return builder.CreateEndNode(gotoDefaultStatement);
531      }
532     
533      public override ControlFlowNode VisitWhileStatement(WhileStatement whileStatement, ControlFlowNode data)
534      {
535        // <data> <condition> while (cond) { <bodyStart> embeddedStmt; <bodyEnd> } <end>
536        ControlFlowNode end = builder.CreateEndNode(whileStatement, addToNodeList: false);
537        ControlFlowNode conditionNode = builder.CreateSpecialNode(whileStatement, ControlFlowNodeType.LoopCondition);
538        breakTargets.Push(end);
539        continueTargets.Push(conditionNode);
540       
541        Connect(data, conditionNode);
542       
543        bool? cond = builder.EvaluateCondition(whileStatement.Condition);
544        ControlFlowNode bodyStart = builder.CreateStartNode(whileStatement.EmbeddedStatement);
545        if (cond != false)
546          Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue);
547        ControlFlowNode bodyEnd = whileStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart);
548        Connect(bodyEnd, conditionNode);
549        if (cond != true)
550          Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse);
551       
552        breakTargets.Pop();
553        continueTargets.Pop();
554        builder.nodes.Add(end);
555        return end;
556      }
557     
558      public override ControlFlowNode VisitDoWhileStatement(DoWhileStatement doWhileStatement, ControlFlowNode data)
559      {
560        // <data> do { <bodyStart> embeddedStmt; <bodyEnd>} <condition> while(cond); <end>
561        ControlFlowNode end = builder.CreateEndNode(doWhileStatement, addToNodeList: false);
562        ControlFlowNode conditionNode = builder.CreateSpecialNode(doWhileStatement, ControlFlowNodeType.LoopCondition, addToNodeList: false);
563        breakTargets.Push(end);
564        continueTargets.Push(conditionNode);
565       
566        ControlFlowNode bodyStart = builder.CreateStartNode(doWhileStatement.EmbeddedStatement);
567        Connect(data, bodyStart);
568        ControlFlowNode bodyEnd = doWhileStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart);
569        Connect(bodyEnd, conditionNode);
570       
571        bool? cond = builder.EvaluateCondition(doWhileStatement.Condition);
572        if (cond != false)
573          Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue);
574        if (cond != true)
575          Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse);
576       
577        breakTargets.Pop();
578        continueTargets.Pop();
579        builder.nodes.Add(conditionNode);
580        builder.nodes.Add(end);
581        return end;
582      }
583     
584      public override ControlFlowNode VisitForStatement(ForStatement forStatement, ControlFlowNode data)
585      {
586        data = HandleStatementList(forStatement.Initializers, data);
587        // for (initializers <data>; <condition>cond; <iteratorStart>iterators<iteratorEnd>) { <bodyStart> embeddedStmt; <bodyEnd> } <end>
588        ControlFlowNode end = builder.CreateEndNode(forStatement, addToNodeList: false);
589        ControlFlowNode conditionNode = builder.CreateSpecialNode(forStatement, ControlFlowNodeType.LoopCondition);
590        Connect(data, conditionNode);
591       
592        int iteratorStartNodeID = builder.nodes.Count;
593        ControlFlowNode iteratorEnd = HandleStatementList(forStatement.Iterators, null);
594        ControlFlowNode iteratorStart;
595        if (iteratorEnd != null) {
596          iteratorStart = builder.nodes[iteratorStartNodeID];
597          Connect(iteratorEnd, conditionNode);
598        } else {
599          iteratorStart = conditionNode;
600        }
601       
602        breakTargets.Push(end);
603        continueTargets.Push(iteratorStart);
604       
605        ControlFlowNode bodyStart = builder.CreateStartNode(forStatement.EmbeddedStatement);
606        ControlFlowNode bodyEnd = forStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart);
607        Connect(bodyEnd, iteratorStart);
608       
609        breakTargets.Pop();
610        continueTargets.Pop();
611       
612        bool? cond = forStatement.Condition.IsNull ? true : builder.EvaluateCondition(forStatement.Condition);
613        if (cond != false)
614          Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue);
615        if (cond != true)
616          Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse);
617       
618        builder.nodes.Add(end);
619        return end;
620      }
621     
622      ControlFlowNode HandleEmbeddedStatement(Statement embeddedStatement, ControlFlowNode source)
623      {
624        if (embeddedStatement == null || embeddedStatement.IsNull)
625          return source;
626        ControlFlowNode bodyStart = builder.CreateStartNode(embeddedStatement);
627        if (source != null)
628          Connect(source, bodyStart);
629        return embeddedStatement.AcceptVisitor(this, bodyStart);
630      }
631     
632      public override ControlFlowNode VisitForeachStatement(ForeachStatement foreachStatement, ControlFlowNode data)
633      {
634        // <data> foreach (<condition>...) { <bodyStart>embeddedStmt<bodyEnd> } <end>
635        ControlFlowNode end = builder.CreateEndNode(foreachStatement, addToNodeList: false);
636        ControlFlowNode conditionNode = builder.CreateSpecialNode(foreachStatement, ControlFlowNodeType.LoopCondition);
637        Connect(data, conditionNode);
638       
639        breakTargets.Push(end);
640        continueTargets.Push(conditionNode);
641       
642        ControlFlowNode bodyEnd = HandleEmbeddedStatement(foreachStatement.EmbeddedStatement, conditionNode);
643        Connect(bodyEnd, conditionNode);
644       
645        breakTargets.Pop();
646        continueTargets.Pop();
647       
648        Connect(conditionNode, end);
649        builder.nodes.Add(end);
650        return end;
651      }
652     
653      public override ControlFlowNode VisitBreakStatement(BreakStatement breakStatement, ControlFlowNode data)
654      {
655        if (breakTargets.Count > 0)
656          Connect(data, breakTargets.Peek(), ControlFlowEdgeType.Jump);
657        return builder.CreateEndNode(breakStatement);
658      }
659     
660      public override ControlFlowNode VisitContinueStatement(ContinueStatement continueStatement, ControlFlowNode data)
661      {
662        if (continueTargets.Count > 0)
663          Connect(data, continueTargets.Peek(), ControlFlowEdgeType.Jump);
664        return builder.CreateEndNode(continueStatement);
665      }
666     
667      public override ControlFlowNode VisitGotoStatement(GotoStatement gotoStatement, ControlFlowNode data)
668      {
669        builder.gotoStatements.Add(data);
670        return builder.CreateEndNode(gotoStatement);
671      }
672     
673      public override ControlFlowNode VisitReturnStatement(ReturnStatement returnStatement, ControlFlowNode data)
674      {
675        return builder.CreateEndNode(returnStatement); // end not connected with data
676      }
677     
678      public override ControlFlowNode VisitThrowStatement(ThrowStatement throwStatement, ControlFlowNode data)
679      {
680        return builder.CreateEndNode(throwStatement); // end not connected with data
681      }
682     
683      public override ControlFlowNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement, ControlFlowNode data)
684      {
685        ControlFlowNode end = builder.CreateEndNode(tryCatchStatement, addToNodeList: false);
686        var edge = Connect(HandleEmbeddedStatement(tryCatchStatement.TryBlock, data), end);
687        if (!tryCatchStatement.FinallyBlock.IsNull)
688          edge.AddJumpOutOfTryFinally(tryCatchStatement);
689        foreach (CatchClause cc in tryCatchStatement.CatchClauses) {
690          edge = Connect(HandleEmbeddedStatement(cc.Body, data), end);
691          if (!tryCatchStatement.FinallyBlock.IsNull)
692            edge.AddJumpOutOfTryFinally(tryCatchStatement);
693        }
694        if (!tryCatchStatement.FinallyBlock.IsNull) {
695          // Don't connect the end of the try-finally block to anything.
696          // Consumers of the CFG will have to special-case try-finally.
697          HandleEmbeddedStatement(tryCatchStatement.FinallyBlock, data);
698        }
699        builder.nodes.Add(end);
700        return end;
701      }
702     
703      public override ControlFlowNode VisitCheckedStatement(CheckedStatement checkedStatement, ControlFlowNode data)
704      {
705        ControlFlowNode bodyEnd = HandleEmbeddedStatement(checkedStatement.Body, data);
706        return CreateConnectedEndNode(checkedStatement, bodyEnd);
707      }
708     
709      public override ControlFlowNode VisitUncheckedStatement(UncheckedStatement uncheckedStatement, ControlFlowNode data)
710      {
711        ControlFlowNode bodyEnd = HandleEmbeddedStatement(uncheckedStatement.Body, data);
712        return CreateConnectedEndNode(uncheckedStatement, bodyEnd);
713      }
714     
715      public override ControlFlowNode VisitLockStatement(LockStatement lockStatement, ControlFlowNode data)
716      {
717        ControlFlowNode bodyEnd = HandleEmbeddedStatement(lockStatement.EmbeddedStatement, data);
718        return CreateConnectedEndNode(lockStatement, bodyEnd);
719      }
720     
721      public override ControlFlowNode VisitUsingStatement(UsingStatement usingStatement, ControlFlowNode data)
722      {
723        data = HandleEmbeddedStatement(usingStatement.ResourceAcquisition as Statement, data);
724        ControlFlowNode bodyEnd = HandleEmbeddedStatement(usingStatement.EmbeddedStatement, data);
725        return CreateConnectedEndNode(usingStatement, bodyEnd);
726      }
727     
728      public override ControlFlowNode VisitYieldReturnStatement(YieldReturnStatement yieldStatement, ControlFlowNode data)
729      {
730        return CreateConnectedEndNode(yieldStatement, data);
731      }
732     
733      public override ControlFlowNode VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, ControlFlowNode data)
734      {
735        return builder.CreateEndNode(yieldBreakStatement); // end not connected with data
736      }
737     
738      public override ControlFlowNode VisitUnsafeStatement(UnsafeStatement unsafeStatement, ControlFlowNode data)
739      {
740        ControlFlowNode bodyEnd = HandleEmbeddedStatement(unsafeStatement.Body, data);
741        return CreateConnectedEndNode(unsafeStatement, bodyEnd);
742      }
743     
744      public override ControlFlowNode VisitFixedStatement(FixedStatement fixedStatement, ControlFlowNode data)
745      {
746        ControlFlowNode bodyEnd = HandleEmbeddedStatement(fixedStatement.EmbeddedStatement, data);
747        return CreateConnectedEndNode(fixedStatement, bodyEnd);
748      }
749    }
750   
751    /// <summary>
752    /// Debugging helper that exports a control flow graph.
753    /// </summary>
754    public static GraphVizGraph ExportGraph(IList<ControlFlowNode> nodes)
755    {
756      GraphVizGraph g = new GraphVizGraph();
757      GraphVizNode[] n = new GraphVizNode[nodes.Count];
758      Dictionary<ControlFlowNode, int> dict = new Dictionary<ControlFlowNode, int>();
759      for (int i = 0; i < n.Length; i++) {
760        dict.Add(nodes[i], i);
761        n[i] = new GraphVizNode(i);
762        string name = "#" + i + " = ";
763        switch (nodes[i].Type) {
764          case ControlFlowNodeType.StartNode:
765          case ControlFlowNodeType.BetweenStatements:
766            name += nodes[i].NextStatement.DebugToString();
767            break;
768          case ControlFlowNodeType.EndNode:
769            name += "End of " + nodes[i].PreviousStatement.DebugToString();
770            break;
771          case ControlFlowNodeType.LoopCondition:
772            name += "Condition in " + nodes[i].NextStatement.DebugToString();
773            break;
774          default:
775            name += "?";
776            break;
777        }
778        n[i].label = name;
779        g.AddNode(n[i]);
780      }
781      for (int i = 0; i < n.Length; i++) {
782        foreach (ControlFlowEdge edge in nodes[i].Outgoing) {
783          GraphVizEdge ge = new GraphVizEdge(i, dict[edge.To]);
784          if (edge.IsLeavingTryFinally)
785            ge.style = "dashed";
786          switch (edge.Type) {
787            case ControlFlowEdgeType.ConditionTrue:
788              ge.color = "green";
789              break;
790            case ControlFlowEdgeType.ConditionFalse:
791              ge.color = "red";
792              break;
793            case ControlFlowEdgeType.Jump:
794              ge.color = "blue";
795              break;
796          }
797          g.AddEdge(ge);
798        }
799      }
800      return g;
801    }
802  }
803}
Note: See TracBrowser for help on using the repository browser.