// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.CSharp.TypeSystem; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem.Implementation; using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.CSharp.Analysis { /// /// Represents a node in the control flow graph of a C# method. /// public class ControlFlowNode { public readonly Statement PreviousStatement; public readonly Statement NextStatement; public readonly ControlFlowNodeType Type; public readonly List Outgoing = new List(); public readonly List Incoming = new List(); public ControlFlowNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) { if (previousStatement == null && nextStatement == null) throw new ArgumentException("previousStatement and nextStatement must not be both null"); this.PreviousStatement = previousStatement; this.NextStatement = nextStatement; this.Type = type; } } public enum ControlFlowNodeType { /// /// Unknown node type /// None, /// /// Node in front of a statement /// StartNode, /// /// Node between two statements /// BetweenStatements, /// /// Node at the end of a statement list /// EndNode, /// /// Node representing the position before evaluating the condition of a loop. /// LoopCondition } public class ControlFlowEdge { public readonly ControlFlowNode From; public readonly ControlFlowNode To; public readonly ControlFlowEdgeType Type; List jumpOutOfTryFinally; public ControlFlowEdge(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type) { if (from == null) throw new ArgumentNullException("from"); if (to == null) throw new ArgumentNullException("to"); this.From = from; this.To = to; this.Type = type; } internal void AddJumpOutOfTryFinally(TryCatchStatement tryFinally) { if (jumpOutOfTryFinally == null) jumpOutOfTryFinally = new List(); jumpOutOfTryFinally.Add(tryFinally); } /// /// Gets whether this control flow edge is leaving any try-finally statements. /// public bool IsLeavingTryFinally { get { return jumpOutOfTryFinally != null; } } /// /// Gets the try-finally statements that this control flow edge is leaving. /// public IEnumerable TryFinallyStatements { get { return jumpOutOfTryFinally ?? Enumerable.Empty(); } } } public enum ControlFlowEdgeType { /// /// Regular control flow. /// Normal, /// /// Conditional control flow (edge taken if condition is true) /// ConditionTrue, /// /// Conditional control flow (edge taken if condition is false) /// ConditionFalse, /// /// A jump statement (goto, goto case, break or continue) /// Jump } /// /// Constructs the control flow graph for C# statements. /// public class ControlFlowGraphBuilder { // Written according to the reachability rules in the C# spec (§8.1 End points and reachability) protected virtual ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) { cancellationToken.ThrowIfCancellationRequested(); return new ControlFlowNode(previousStatement, nextStatement, type); } protected virtual ControlFlowEdge CreateEdge(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type) { cancellationToken.ThrowIfCancellationRequested(); return new ControlFlowEdge(from, to, type); } Statement rootStatement; CSharpTypeResolveContext typeResolveContext; Func resolver; List nodes; Dictionary labels; List gotoStatements; CancellationToken cancellationToken; public IList BuildControlFlowGraph(Statement statement, CancellationToken cancellationToken = default(CancellationToken)) { if (statement == null) throw new ArgumentNullException("statement"); CSharpResolver r = new CSharpResolver(MinimalCorlib.Instance.CreateCompilation()); return BuildControlFlowGraph(statement, new CSharpAstResolver(r, statement), cancellationToken); } public IList BuildControlFlowGraph(Statement statement, CSharpAstResolver resolver, CancellationToken cancellationToken = default(CancellationToken)) { if (statement == null) throw new ArgumentNullException("statement"); if (resolver == null) throw new ArgumentNullException("resolver"); return BuildControlFlowGraph(statement, resolver.Resolve, resolver.TypeResolveContext, cancellationToken); } internal IList BuildControlFlowGraph(Statement statement, Func resolver, CSharpTypeResolveContext typeResolveContext, CancellationToken cancellationToken) { NodeCreationVisitor nodeCreationVisitor = new NodeCreationVisitor(); nodeCreationVisitor.builder = this; try { this.nodes = new List(); this.labels = new Dictionary(); this.gotoStatements = new List(); this.rootStatement = statement; this.resolver = resolver; this.typeResolveContext = typeResolveContext; this.cancellationToken = cancellationToken; ControlFlowNode entryPoint = CreateStartNode(statement); statement.AcceptVisitor(nodeCreationVisitor, entryPoint); // Resolve goto statements: foreach (ControlFlowNode gotoStmt in gotoStatements) { string label = ((GotoStatement)gotoStmt.NextStatement).Label; ControlFlowNode labelNode; if (labels.TryGetValue(label, out labelNode)) nodeCreationVisitor.Connect(gotoStmt, labelNode, ControlFlowEdgeType.Jump); } AnnotateLeaveEdgesWithTryFinallyBlocks(); return nodes; } finally { this.nodes = null; this.labels = null; this.gotoStatements = null; this.rootStatement = null; this.resolver = null; this.typeResolveContext = null; this.cancellationToken = CancellationToken.None; } } void AnnotateLeaveEdgesWithTryFinallyBlocks() { foreach (ControlFlowEdge edge in nodes.SelectMany(n => n.Outgoing)) { if (edge.Type != ControlFlowEdgeType.Jump) { // Only jumps are potential candidates for leaving try-finally blocks. // Note that the regular edges leaving try or catch blocks are already annotated by the visitor. continue; } Statement gotoStatement = edge.From.NextStatement; Debug.Assert(gotoStatement is GotoStatement || gotoStatement is GotoDefaultStatement || gotoStatement is GotoCaseStatement || gotoStatement is BreakStatement || gotoStatement is ContinueStatement); Statement targetStatement = edge.To.PreviousStatement ?? edge.To.NextStatement; if (gotoStatement.Parent == targetStatement.Parent) continue; HashSet targetParentTryCatch = new HashSet(targetStatement.Ancestors.OfType()); for (AstNode node = gotoStatement.Parent; node != null; node = node.Parent) { TryCatchStatement leftTryCatch = node as TryCatchStatement; if (leftTryCatch != null) { if (targetParentTryCatch.Contains(leftTryCatch)) break; if (!leftTryCatch.FinallyBlock.IsNull) edge.AddJumpOutOfTryFinally(leftTryCatch); } } } } #region Create*Node ControlFlowNode CreateStartNode(Statement statement) { if (statement.IsNull) return null; ControlFlowNode node = CreateNode(null, statement, ControlFlowNodeType.StartNode); nodes.Add(node); return node; } ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type, bool addToNodeList = true) { ControlFlowNode node = CreateNode(null, statement, type); if (addToNodeList) nodes.Add(node); return node; } ControlFlowNode CreateEndNode(Statement statement, bool addToNodeList = true) { Statement nextStatement; if (statement == rootStatement) { nextStatement = null; } else { // Find the next statement in the same role: AstNode next = statement; do { next = next.NextSibling; } while (next != null && next.Role != statement.Role); nextStatement = next as Statement; } ControlFlowNodeType type = nextStatement != null ? ControlFlowNodeType.BetweenStatements : ControlFlowNodeType.EndNode; ControlFlowNode node = CreateNode(statement, nextStatement, type); if (addToNodeList) nodes.Add(node); return node; } #endregion #region Constant evaluation /// /// Gets/Sets whether to handle only primitive expressions as constants (no complex expressions like "a + b"). /// public bool EvaluateOnlyPrimitiveConstants { get; set; } /// /// Evaluates an expression. /// /// The constant value of the expression; or null if the expression is not a constant. ResolveResult EvaluateConstant(Expression expr) { if (expr.IsNull) return null; if (EvaluateOnlyPrimitiveConstants) { if (!(expr is PrimitiveExpression || expr is NullReferenceExpression)) return null; } return resolver(expr, cancellationToken); } /// /// Evaluates an expression. /// /// The value of the constant boolean expression; or null if the value is not a constant boolean expression. bool? EvaluateCondition(Expression expr) { ResolveResult rr = EvaluateConstant(expr); if (rr != null && rr.IsCompileTimeConstant) return rr.ConstantValue as bool?; else return null; } bool AreEqualConstants(ResolveResult c1, ResolveResult c2) { if (c1 == null || c2 == null || !c1.IsCompileTimeConstant || !c2.IsCompileTimeConstant) return false; CSharpResolver r = new CSharpResolver(typeResolveContext); ResolveResult c = r.ResolveBinaryOperator(BinaryOperatorType.Equality, c1, c2); return c.IsCompileTimeConstant && (c.ConstantValue as bool?) == true; } #endregion sealed class NodeCreationVisitor : DepthFirstAstVisitor { // 'data' parameter: input control flow node (start of statement being visited) // Return value: result control flow node (end of statement being visited) internal ControlFlowGraphBuilder builder; Stack breakTargets = new Stack(); Stack continueTargets = new Stack(); List gotoCaseOrDefault = new List(); internal ControlFlowEdge Connect(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type = ControlFlowEdgeType.Normal) { if (from == null || to == null) return null; ControlFlowEdge edge = builder.CreateEdge(from, to, type); from.Outgoing.Add(edge); to.Incoming.Add(edge); return edge; } /// /// Creates an end node for stmt and connects from with the new node. /// ControlFlowNode CreateConnectedEndNode(Statement stmt, ControlFlowNode from) { ControlFlowNode newNode = builder.CreateEndNode(stmt); Connect(from, newNode); return newNode; } protected override ControlFlowNode VisitChildren(AstNode node, ControlFlowNode data) { // We have overrides for all possible statements and should visit statements only. throw new NotSupportedException(); } public override ControlFlowNode VisitBlockStatement(BlockStatement blockStatement, ControlFlowNode data) { // C# 4.0 spec: §8.2 Blocks ControlFlowNode childNode = HandleStatementList(blockStatement.Statements, data); return CreateConnectedEndNode(blockStatement, childNode); } ControlFlowNode HandleStatementList(AstNodeCollection statements, ControlFlowNode source) { ControlFlowNode childNode = null; foreach (Statement stmt in statements) { if (childNode == null) { childNode = builder.CreateStartNode(stmt); if (source != null) Connect(source, childNode); } Debug.Assert(childNode.NextStatement == stmt); childNode = stmt.AcceptVisitor(this, childNode); Debug.Assert(childNode.PreviousStatement == stmt); } return childNode ?? source; } public override ControlFlowNode VisitEmptyStatement(EmptyStatement emptyStatement, ControlFlowNode data) { return CreateConnectedEndNode(emptyStatement, data); } public override ControlFlowNode VisitLabelStatement(LabelStatement labelStatement, ControlFlowNode data) { ControlFlowNode end = CreateConnectedEndNode(labelStatement, data); builder.labels[labelStatement.Label] = end; return end; } public override ControlFlowNode VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, ControlFlowNode data) { return CreateConnectedEndNode(variableDeclarationStatement, data); } public override ControlFlowNode VisitExpressionStatement(ExpressionStatement expressionStatement, ControlFlowNode data) { return CreateConnectedEndNode(expressionStatement, data); } public override ControlFlowNode VisitIfElseStatement(IfElseStatement ifElseStatement, ControlFlowNode data) { bool? cond = builder.EvaluateCondition(ifElseStatement.Condition); ControlFlowNode trueBegin = builder.CreateStartNode(ifElseStatement.TrueStatement); if (cond != false) Connect(data, trueBegin, ControlFlowEdgeType.ConditionTrue); ControlFlowNode trueEnd = ifElseStatement.TrueStatement.AcceptVisitor(this, trueBegin); ControlFlowNode falseBegin = builder.CreateStartNode(ifElseStatement.FalseStatement); if (cond != true) Connect(data, falseBegin, ControlFlowEdgeType.ConditionFalse); ControlFlowNode falseEnd = ifElseStatement.FalseStatement.AcceptVisitor(this, falseBegin); // (if no else statement exists, both falseBegin and falseEnd will be null) ControlFlowNode end = builder.CreateEndNode(ifElseStatement); Connect(trueEnd, end); if (falseEnd != null) { Connect(falseEnd, end); } else if (cond != true) { Connect(data, end, ControlFlowEdgeType.ConditionFalse); } return end; } public override ControlFlowNode VisitSwitchStatement(SwitchStatement switchStatement, ControlFlowNode data) { // First, figure out which switch section will get called (if the expression is constant): ResolveResult constant = builder.EvaluateConstant(switchStatement.Expression); SwitchSection defaultSection = null; SwitchSection sectionMatchedByConstant = null; foreach (SwitchSection section in switchStatement.SwitchSections) { foreach (CaseLabel label in section.CaseLabels) { if (label.Expression.IsNull) { defaultSection = section; } else if (constant != null && constant.IsCompileTimeConstant) { ResolveResult labelConstant = builder.EvaluateConstant(label.Expression); if (builder.AreEqualConstants(constant, labelConstant)) sectionMatchedByConstant = section; } } } if (constant != null && constant.IsCompileTimeConstant && sectionMatchedByConstant == null) sectionMatchedByConstant = defaultSection; int gotoCaseOrDefaultInOuterScope = gotoCaseOrDefault.Count; List sectionStartNodes = new List(); ControlFlowNode end = builder.CreateEndNode(switchStatement, addToNodeList: false); breakTargets.Push(end); foreach (SwitchSection section in switchStatement.SwitchSections) { int sectionStartNodeID = builder.nodes.Count; if (constant == null || !constant.IsCompileTimeConstant || section == sectionMatchedByConstant) { HandleStatementList(section.Statements, data); } else { // This section is unreachable: pass null to HandleStatementList. HandleStatementList(section.Statements, null); } // Don't bother connecting the ends of the sections: the 'break' statement takes care of that. // Store the section start node for 'goto case' statements. sectionStartNodes.Add(sectionStartNodeID < builder.nodes.Count ? builder.nodes[sectionStartNodeID] : null); } breakTargets.Pop(); if (defaultSection == null && sectionMatchedByConstant == null) { Connect(data, end); } if (gotoCaseOrDefault.Count > gotoCaseOrDefaultInOuterScope) { // Resolve 'goto case' statements: for (int i = gotoCaseOrDefaultInOuterScope; i < gotoCaseOrDefault.Count; i++) { ControlFlowNode gotoCaseNode = gotoCaseOrDefault[i]; GotoCaseStatement gotoCaseStatement = gotoCaseNode.NextStatement as GotoCaseStatement; ResolveResult gotoCaseConstant = null; if (gotoCaseStatement != null) { gotoCaseConstant = builder.EvaluateConstant(gotoCaseStatement.LabelExpression); } int targetSectionIndex = -1; int currentSectionIndex = 0; foreach (SwitchSection section in switchStatement.SwitchSections) { foreach (CaseLabel label in section.CaseLabels) { if (gotoCaseStatement != null) { // goto case if (!label.Expression.IsNull) { ResolveResult labelConstant = builder.EvaluateConstant(label.Expression); if (builder.AreEqualConstants(gotoCaseConstant, labelConstant)) targetSectionIndex = currentSectionIndex; } } else { // goto default if (label.Expression.IsNull) targetSectionIndex = currentSectionIndex; } } currentSectionIndex++; } if (targetSectionIndex >= 0 && sectionStartNodes[targetSectionIndex] != null) Connect(gotoCaseNode, sectionStartNodes[targetSectionIndex], ControlFlowEdgeType.Jump); else Connect(gotoCaseNode, end, ControlFlowEdgeType.Jump); } gotoCaseOrDefault.RemoveRange(gotoCaseOrDefaultInOuterScope, gotoCaseOrDefault.Count - gotoCaseOrDefaultInOuterScope); } builder.nodes.Add(end); return end; } public override ControlFlowNode VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement, ControlFlowNode data) { gotoCaseOrDefault.Add(data); return builder.CreateEndNode(gotoCaseStatement); } public override ControlFlowNode VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement, ControlFlowNode data) { gotoCaseOrDefault.Add(data); return builder.CreateEndNode(gotoDefaultStatement); } public override ControlFlowNode VisitWhileStatement(WhileStatement whileStatement, ControlFlowNode data) { // while (cond) { embeddedStmt; } ControlFlowNode end = builder.CreateEndNode(whileStatement, addToNodeList: false); ControlFlowNode conditionNode = builder.CreateSpecialNode(whileStatement, ControlFlowNodeType.LoopCondition); breakTargets.Push(end); continueTargets.Push(conditionNode); Connect(data, conditionNode); bool? cond = builder.EvaluateCondition(whileStatement.Condition); ControlFlowNode bodyStart = builder.CreateStartNode(whileStatement.EmbeddedStatement); if (cond != false) Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue); ControlFlowNode bodyEnd = whileStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart); Connect(bodyEnd, conditionNode); if (cond != true) Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse); breakTargets.Pop(); continueTargets.Pop(); builder.nodes.Add(end); return end; } public override ControlFlowNode VisitDoWhileStatement(DoWhileStatement doWhileStatement, ControlFlowNode data) { // do { embeddedStmt; } while(cond); ControlFlowNode end = builder.CreateEndNode(doWhileStatement, addToNodeList: false); ControlFlowNode conditionNode = builder.CreateSpecialNode(doWhileStatement, ControlFlowNodeType.LoopCondition, addToNodeList: false); breakTargets.Push(end); continueTargets.Push(conditionNode); ControlFlowNode bodyStart = builder.CreateStartNode(doWhileStatement.EmbeddedStatement); Connect(data, bodyStart); ControlFlowNode bodyEnd = doWhileStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart); Connect(bodyEnd, conditionNode); bool? cond = builder.EvaluateCondition(doWhileStatement.Condition); if (cond != false) Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue); if (cond != true) Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse); breakTargets.Pop(); continueTargets.Pop(); builder.nodes.Add(conditionNode); builder.nodes.Add(end); return end; } public override ControlFlowNode VisitForStatement(ForStatement forStatement, ControlFlowNode data) { data = HandleStatementList(forStatement.Initializers, data); // for (initializers ; cond; iterators) { embeddedStmt; } ControlFlowNode end = builder.CreateEndNode(forStatement, addToNodeList: false); ControlFlowNode conditionNode = builder.CreateSpecialNode(forStatement, ControlFlowNodeType.LoopCondition); Connect(data, conditionNode); int iteratorStartNodeID = builder.nodes.Count; ControlFlowNode iteratorEnd = HandleStatementList(forStatement.Iterators, null); ControlFlowNode iteratorStart; if (iteratorEnd != null) { iteratorStart = builder.nodes[iteratorStartNodeID]; Connect(iteratorEnd, conditionNode); } else { iteratorStart = conditionNode; } breakTargets.Push(end); continueTargets.Push(iteratorStart); ControlFlowNode bodyStart = builder.CreateStartNode(forStatement.EmbeddedStatement); ControlFlowNode bodyEnd = forStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart); Connect(bodyEnd, iteratorStart); breakTargets.Pop(); continueTargets.Pop(); bool? cond = forStatement.Condition.IsNull ? true : builder.EvaluateCondition(forStatement.Condition); if (cond != false) Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue); if (cond != true) Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse); builder.nodes.Add(end); return end; } ControlFlowNode HandleEmbeddedStatement(Statement embeddedStatement, ControlFlowNode source) { if (embeddedStatement == null || embeddedStatement.IsNull) return source; ControlFlowNode bodyStart = builder.CreateStartNode(embeddedStatement); if (source != null) Connect(source, bodyStart); return embeddedStatement.AcceptVisitor(this, bodyStart); } public override ControlFlowNode VisitForeachStatement(ForeachStatement foreachStatement, ControlFlowNode data) { // foreach (...) { embeddedStmt } ControlFlowNode end = builder.CreateEndNode(foreachStatement, addToNodeList: false); ControlFlowNode conditionNode = builder.CreateSpecialNode(foreachStatement, ControlFlowNodeType.LoopCondition); Connect(data, conditionNode); breakTargets.Push(end); continueTargets.Push(conditionNode); ControlFlowNode bodyEnd = HandleEmbeddedStatement(foreachStatement.EmbeddedStatement, conditionNode); Connect(bodyEnd, conditionNode); breakTargets.Pop(); continueTargets.Pop(); Connect(conditionNode, end); builder.nodes.Add(end); return end; } public override ControlFlowNode VisitBreakStatement(BreakStatement breakStatement, ControlFlowNode data) { if (breakTargets.Count > 0) Connect(data, breakTargets.Peek(), ControlFlowEdgeType.Jump); return builder.CreateEndNode(breakStatement); } public override ControlFlowNode VisitContinueStatement(ContinueStatement continueStatement, ControlFlowNode data) { if (continueTargets.Count > 0) Connect(data, continueTargets.Peek(), ControlFlowEdgeType.Jump); return builder.CreateEndNode(continueStatement); } public override ControlFlowNode VisitGotoStatement(GotoStatement gotoStatement, ControlFlowNode data) { builder.gotoStatements.Add(data); return builder.CreateEndNode(gotoStatement); } public override ControlFlowNode VisitReturnStatement(ReturnStatement returnStatement, ControlFlowNode data) { return builder.CreateEndNode(returnStatement); // end not connected with data } public override ControlFlowNode VisitThrowStatement(ThrowStatement throwStatement, ControlFlowNode data) { return builder.CreateEndNode(throwStatement); // end not connected with data } public override ControlFlowNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement, ControlFlowNode data) { ControlFlowNode end = builder.CreateEndNode(tryCatchStatement, addToNodeList: false); var edge = Connect(HandleEmbeddedStatement(tryCatchStatement.TryBlock, data), end); if (!tryCatchStatement.FinallyBlock.IsNull) edge.AddJumpOutOfTryFinally(tryCatchStatement); foreach (CatchClause cc in tryCatchStatement.CatchClauses) { edge = Connect(HandleEmbeddedStatement(cc.Body, data), end); if (!tryCatchStatement.FinallyBlock.IsNull) edge.AddJumpOutOfTryFinally(tryCatchStatement); } if (!tryCatchStatement.FinallyBlock.IsNull) { // Don't connect the end of the try-finally block to anything. // Consumers of the CFG will have to special-case try-finally. HandleEmbeddedStatement(tryCatchStatement.FinallyBlock, data); } builder.nodes.Add(end); return end; } public override ControlFlowNode VisitCheckedStatement(CheckedStatement checkedStatement, ControlFlowNode data) { ControlFlowNode bodyEnd = HandleEmbeddedStatement(checkedStatement.Body, data); return CreateConnectedEndNode(checkedStatement, bodyEnd); } public override ControlFlowNode VisitUncheckedStatement(UncheckedStatement uncheckedStatement, ControlFlowNode data) { ControlFlowNode bodyEnd = HandleEmbeddedStatement(uncheckedStatement.Body, data); return CreateConnectedEndNode(uncheckedStatement, bodyEnd); } public override ControlFlowNode VisitLockStatement(LockStatement lockStatement, ControlFlowNode data) { ControlFlowNode bodyEnd = HandleEmbeddedStatement(lockStatement.EmbeddedStatement, data); return CreateConnectedEndNode(lockStatement, bodyEnd); } public override ControlFlowNode VisitUsingStatement(UsingStatement usingStatement, ControlFlowNode data) { data = HandleEmbeddedStatement(usingStatement.ResourceAcquisition as Statement, data); ControlFlowNode bodyEnd = HandleEmbeddedStatement(usingStatement.EmbeddedStatement, data); return CreateConnectedEndNode(usingStatement, bodyEnd); } public override ControlFlowNode VisitYieldReturnStatement(YieldReturnStatement yieldStatement, ControlFlowNode data) { return CreateConnectedEndNode(yieldStatement, data); } public override ControlFlowNode VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, ControlFlowNode data) { return builder.CreateEndNode(yieldBreakStatement); // end not connected with data } public override ControlFlowNode VisitUnsafeStatement(UnsafeStatement unsafeStatement, ControlFlowNode data) { ControlFlowNode bodyEnd = HandleEmbeddedStatement(unsafeStatement.Body, data); return CreateConnectedEndNode(unsafeStatement, bodyEnd); } public override ControlFlowNode VisitFixedStatement(FixedStatement fixedStatement, ControlFlowNode data) { ControlFlowNode bodyEnd = HandleEmbeddedStatement(fixedStatement.EmbeddedStatement, data); return CreateConnectedEndNode(fixedStatement, bodyEnd); } } /// /// Debugging helper that exports a control flow graph. /// public static GraphVizGraph ExportGraph(IList nodes) { GraphVizGraph g = new GraphVizGraph(); GraphVizNode[] n = new GraphVizNode[nodes.Count]; Dictionary dict = new Dictionary(); for (int i = 0; i < n.Length; i++) { dict.Add(nodes[i], i); n[i] = new GraphVizNode(i); string name = "#" + i + " = "; switch (nodes[i].Type) { case ControlFlowNodeType.StartNode: case ControlFlowNodeType.BetweenStatements: name += nodes[i].NextStatement.DebugToString(); break; case ControlFlowNodeType.EndNode: name += "End of " + nodes[i].PreviousStatement.DebugToString(); break; case ControlFlowNodeType.LoopCondition: name += "Condition in " + nodes[i].NextStatement.DebugToString(); break; default: name += "?"; break; } n[i].label = name; g.AddNode(n[i]); } for (int i = 0; i < n.Length; i++) { foreach (ControlFlowEdge edge in nodes[i].Outgoing) { GraphVizEdge ge = new GraphVizEdge(i, dict[edge.To]); if (edge.IsLeavingTryFinally) ge.style = "dashed"; switch (edge.Type) { case ControlFlowEdgeType.ConditionTrue: ge.color = "green"; break; case ControlFlowEdgeType.ConditionFalse: ge.color = "red"; break; case ControlFlowEdgeType.Jump: ge.color = "blue"; break; } g.AddEdge(ge); } } return g; } } }