Browse Source

Merge branch 'master' of git://github.com/icsharpcode/ILSpy into Debugger

pull/191/merge
Eusebiu Marcu 15 years ago
parent
commit
9eb80d318e
  1. 4
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs
  2. 1
      ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs
  3. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  4. 8
      ILSpy.sln
  5. 2
      ILSpy/MainWindow.xaml.cs
  6. 132
      NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs
  7. 2
      NRefactory/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
  8. 638
      NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs
  9. 640
      NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs
  10. 7
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs
  11. 8
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/DepthFirstAstVisitor.cs
  12. 2
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/Expression.cs
  13. 15
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/Statement.cs
  14. 6
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/Accessor.cs
  15. 14
      NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj
  16. 4
      NRefactory/ICSharpCode.NRefactory/Utils/DotNet35Compat.cs
  17. 43
      NRefactory/ICSharpCode.NRefactory/Utils/GraphVizGraph.cs

4
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs

@ -23,6 +23,8 @@ using System.Diagnostics; @@ -23,6 +23,8 @@ using System.Diagnostics;
using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.Utils;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
@ -57,7 +59,6 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -57,7 +59,6 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit);
}
#if DEBUG
public GraphVizGraph ExportGraph()
{
GraphVizGraph graph = new GraphVizGraph();
@ -87,7 +88,6 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -87,7 +88,6 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
}
return graph;
}
#endif
/// <summary>
/// Resets "Visited" to false for all nodes in this graph.

1
ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs

@ -22,6 +22,7 @@ using System.Collections.ObjectModel; @@ -22,6 +22,7 @@ using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.Utils;
using Mono.Cecil;
using Mono.Cecil.Cil;

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -88,7 +88,6 @@ @@ -88,7 +88,6 @@
<Compile Include="FlowAnalysis\SsaOptimization.cs" />
<Compile Include="FlowAnalysis\SsaVariable.cs" />
<Compile Include="FlowAnalysis\TransformToSsa.cs" />
<Compile Include="GraphVizGraph.cs" />
<Compile Include="ILAst\InitializerPeepholeTransforms.cs" />
<Compile Include="ILAst\DefaultDictionary.cs" />
<Compile Include="ILAst\GotoRemoval.cs" />

8
ILSpy.sln

@ -48,13 +48,13 @@ Global @@ -48,13 +48,13 @@ Global
{DDE2A481-8271-4EAC-A330-8FA6A38D13D1}.Release|Any CPU.Build.0 = Release|Any CPU
{DDE2A481-8271-4EAC-A330-8FA6A38D13D1}.Release|x86.ActiveCfg = Release|Any CPU
{DDE2A481-8271-4EAC-A330-8FA6A38D13D1}.Release|x86.Build.0 = Release|Any CPU
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Debug|Any CPU.ActiveCfg = net_3_5_Debug|Any CPU
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Debug|Any CPU.ActiveCfg = net_4_0_Debug|Any CPU
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Debug|Any CPU.Build.0 = net_4_0_Debug|Any CPU
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Debug|x86.ActiveCfg = net_3_5_Debug|Any CPU
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Debug|x86.ActiveCfg = net_4_0_Debug|Any CPU
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Debug|x86.Build.0 = net_2_0_Debug|Any CPU
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Release|Any CPU.ActiveCfg = net_3_5_Release|Any CPU
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Release|Any CPU.ActiveCfg = net_4_0_Release|Any CPU
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Release|Any CPU.Build.0 = net_4_0_Release|Any CPU
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Release|x86.ActiveCfg = net_3_5_Release|Any CPU
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Release|x86.ActiveCfg = net_4_0_Release|Any CPU
{D68133BD-1E63-496E-9EDE-4FBDBF77B486}.Release|x86.Build.0 = net_2_0_Debug|Any CPU
{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}.Debug|Any CPU.Build.0 = Debug|Any CPU

2
ILSpy/MainWindow.xaml.cs

@ -278,7 +278,7 @@ namespace ICSharpCode.ILSpy @@ -278,7 +278,7 @@ namespace ICSharpCode.ILSpy
typeof(ICSharpCode.TreeView.SharpTreeView).Assembly,
typeof(Mono.Cecil.AssemblyDefinition).Assembly,
typeof(ICSharpCode.AvalonEdit.TextEditor).Assembly,
typeof(ICSharpCode.Decompiler.GraphVizGraph).Assembly,
typeof(ICSharpCode.Decompiler.Ast.AstBuilder).Assembly,
typeof(MainWindow).Assembly
};
foreach (System.Reflection.Assembly asm in initialAssemblies)

132
NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs

@ -0,0 +1,132 @@ @@ -0,0 +1,132 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Linq;
using NUnit.Framework;
namespace ICSharpCode.NRefactory.CSharp.Analysis
{
[TestFixture]
public class DefiniteAssignmentTests
{
[Test]
public void TryFinally()
{
BlockStatement block = new BlockStatement {
new TryCatchStatement {
TryBlock = new BlockStatement {
new GotoStatement("LABEL"),
new AssignmentExpression(new IdentifierExpression("i"), new PrimitiveExpression(1))
},
CatchClauses = {
new CatchClause {
Body = new BlockStatement {
new AssignmentExpression(new IdentifierExpression("i"), new PrimitiveExpression(3))
}
}
},
FinallyBlock = new BlockStatement {
new AssignmentExpression(new IdentifierExpression("j"), new PrimitiveExpression(5))
}
},
new LabelStatement { Label = "LABEL" },
new EmptyStatement()
};
TryCatchStatement tryCatchStatement = (TryCatchStatement)block.Statements.First();
Statement stmt1 = tryCatchStatement.TryBlock.Statements.ElementAt(1);
Statement stmt3 = tryCatchStatement.CatchClauses.Single().Body.Statements.Single();
Statement stmt5 = tryCatchStatement.FinallyBlock.Statements.Single();
LabelStatement label = (LabelStatement)block.Statements.ElementAt(1);
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(block);
da.Analyze("i");
Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(tryCatchStatement));
Assert.AreEqual(DefiniteAssignmentStatus.CodeUnreachable, da.GetStatusBefore(stmt1));
Assert.AreEqual(DefiniteAssignmentStatus.CodeUnreachable, da.GetStatusAfter(stmt1));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(stmt3));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(stmt3));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(stmt5));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusAfter(stmt5));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(tryCatchStatement));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(label));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusAfter(label));
da.Analyze("j");
Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(tryCatchStatement));
Assert.AreEqual(DefiniteAssignmentStatus.CodeUnreachable, da.GetStatusBefore(stmt1));
Assert.AreEqual(DefiniteAssignmentStatus.CodeUnreachable, da.GetStatusAfter(stmt1));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(stmt3));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusAfter(stmt3));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(stmt5));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(stmt5));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(tryCatchStatement));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(label));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(label));
}
[Test]
public void ConditionalAnd()
{
IfElseStatement ifStmt = new IfElseStatement {
Condition = new BinaryOperatorExpression {
Left = new BinaryOperatorExpression(new IdentifierExpression("x"), BinaryOperatorType.GreaterThan, new PrimitiveExpression(0)),
Operator = BinaryOperatorType.ConditionalAnd,
Right = new BinaryOperatorExpression {
Left = new ParenthesizedExpression {
Expression = new AssignmentExpression {
Left = new IdentifierExpression("i"),
Operator = AssignmentOperatorType.Assign,
Right = new IdentifierExpression("y")
}
},
Operator = BinaryOperatorType.GreaterThanOrEqual,
Right = new PrimitiveExpression(0)
}
},
TrueStatement = new BlockStatement(),
FalseStatement = new BlockStatement()
};
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt);
da.Analyze("i");
Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(ifStmt));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(ifStmt.TrueStatement));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(ifStmt.FalseStatement));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusAfter(ifStmt));
}
[Test]
public void ConditionalOr()
{
IfElseStatement ifStmt = new IfElseStatement {
Condition = new BinaryOperatorExpression {
Left = new BinaryOperatorExpression(new IdentifierExpression("x"), BinaryOperatorType.GreaterThan, new PrimitiveExpression(0)),
Operator = BinaryOperatorType.ConditionalOr,
Right = new BinaryOperatorExpression {
Left = new ParenthesizedExpression {
Expression = new AssignmentExpression {
Left = new IdentifierExpression("i"),
Operator = AssignmentOperatorType.Assign,
Right = new IdentifierExpression("y")
}
},
Operator = BinaryOperatorType.GreaterThanOrEqual,
Right = new PrimitiveExpression(0)
}
},
TrueStatement = new BlockStatement(),
FalseStatement = new BlockStatement()
};
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(ifStmt);
da.Analyze("i");
Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(ifStmt));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(ifStmt.TrueStatement));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(ifStmt.FalseStatement));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusAfter(ifStmt));
}
}
}

2
NRefactory/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

@ -59,6 +59,7 @@ @@ -59,6 +59,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CSharp\Analysis\DefiniteAssignmentTests.cs" />
<Compile Include="CSharp\AstStructureTests.cs" />
<Compile Include="CSharp\InsertParenthesesVisitorTests.cs" />
<Compile Include="CSharp\Parser\GeneralScope\DelegateDeclarationTests.cs" />
@ -162,6 +163,7 @@ @@ -162,6 +163,7 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="CSharp\Analysis" />
<Folder Include="CSharp\Parser\Expression" />
<Folder Include="CSharp\Parser\GeneralScope" />
<Folder Include="CSharp\Parser\TypeMembers" />

638
NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs

@ -0,0 +1,638 @@ @@ -0,0 +1,638 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.CSharp.Resolver;
namespace ICSharpCode.NRefactory.CSharp.Analysis
{
/// <summary>
/// Represents a node in the control flow graph of a C# method.
/// </summary>
public class ControlFlowNode
{
public readonly Statement PreviousStatement;
public readonly Statement NextStatement;
public readonly ControlFlowNodeType Type;
public readonly List<ControlFlowEdge> Outgoing = new List<ControlFlowEdge>();
public readonly List<ControlFlowEdge> Incoming = new List<ControlFlowEdge>();
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
{
/// <summary>
/// Unknown node type
/// </summary>
None,
/// <summary>
/// Node in front of a statement
/// </summary>
StartNode,
/// <summary>
/// Node between two statements
/// </summary>
BetweenStatements,
/// <summary>
/// Node at the end of a statement list
/// </summary>
EndNode,
/// <summary>
/// Node representing the position before evaluating the condition of a loop.
/// </summary>
LoopCondition
}
public class ControlFlowEdge
{
public readonly ControlFlowNode From;
public readonly ControlFlowNode To;
public readonly ControlFlowEdgeType Type;
List<TryCatchStatement> 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<TryCatchStatement>();
jumpOutOfTryFinally.Add(tryFinally);
}
/// <summary>
/// Gets whether this control flow edge is leaving any try-finally statements.
/// </summary>
public bool IsLeavingTryFinally {
get { return jumpOutOfTryFinally != null; }
}
/// <summary>
/// Gets the try-finally statements that this control flow edge is leaving.
/// </summary>
public IEnumerable<TryCatchStatement> TryFinallyStatements {
get { return jumpOutOfTryFinally ?? Enumerable.Empty<TryCatchStatement>(); }
}
}
public enum ControlFlowEdgeType
{
/// <summary>
/// Regular control flow.
/// </summary>
Normal,
/// <summary>
/// Conditional control flow (edge taken if condition is true)
/// </summary>
ConditionTrue,
/// <summary>
/// Conditional control flow (edge taken if condition is false)
/// </summary>
ConditionFalse,
/// <summary>
/// A jump statement (goto, goto case, break or continue)
/// </summary>
Jump
}
/// <summary>
/// Constructs the control flow graph for C# statements.
/// </summary>
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)
{
return new ControlFlowNode(previousStatement, nextStatement, type);
}
protected virtual ControlFlowEdge CreateEdge(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type)
{
return new ControlFlowEdge(from, to, type);
}
Statement rootStatement;
List<ControlFlowNode> nodes;
Dictionary<string, ControlFlowNode> labels;
List<ControlFlowNode> gotoStatements;
public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement)
{
NodeCreationVisitor nodeCreationVisitor = new NodeCreationVisitor();
nodeCreationVisitor.builder = this;
try {
this.nodes = new List<ControlFlowNode>();
this.labels = new Dictionary<string, ControlFlowNode>();
this.gotoStatements = new List<ControlFlowNode>();
this.rootStatement = statement;
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;
}
}
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<TryCatchStatement> targetParentTryCatch = new HashSet<TryCatchStatement>(targetStatement.Ancestors.OfType<TryCatchStatement>());
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)
{
ControlFlowNode node = CreateNode(null, statement, ControlFlowNodeType.StartNode);
nodes.Add(node);
return node;
}
ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type)
{
ControlFlowNode node = CreateNode(statement, null, type);
nodes.Add(node);
return node;
}
ControlFlowNode CreateEndNode(Statement statement)
{
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);
nodes.Add(node);
return node;
}
#endregion
#region Constant evaluation
/// <summary>
/// Evaluates an expression.
/// </summary>
/// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
ConstantResolveResult EvaluateConstant(Expression expr)
{
return null; // TODO: implement this using the C# resolver
}
/// <summary>
/// Evaluates an expression.
/// </summary>
/// <returns>The value of the constant boolean expression; or null if the value is not a constant boolean expression.</returns>
bool? EvaluateCondition(Expression expr)
{
ConstantResolveResult crr = EvaluateConstant(expr);
if (crr != null)
return crr.ConstantValue as bool?;
else
return null;
}
bool AreEqualConstants(ConstantResolveResult c1, ConstantResolveResult c2)
{
return false; // TODO: implement this using the resolver's operator==
}
#endregion
sealed class NodeCreationVisitor : DepthFirstAstVisitor<ControlFlowNode, ControlFlowNode>
{
// '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<ControlFlowNode> breakTargets = new Stack<ControlFlowNode>();
Stack<ControlFlowNode> continueTargets = new Stack<ControlFlowNode>();
List<ControlFlowNode> gotoCaseOrDefault = new List<ControlFlowNode>();
internal ControlFlowEdge Connect(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type = ControlFlowEdgeType.Normal)
{
ControlFlowEdge edge = builder.CreateEdge(from, to, type);
from.Outgoing.Add(edge);
to.Incoming.Add(edge);
return edge;
}
/// <summary>
/// Creates an end node for <c>stmt</c> and connects <c>from</c> with the new node.
/// </summary>
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 expressions and should visit expressions only.
throw new NotImplementedException();
}
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<Statement> statements, ControlFlowNode source)
{
ControlFlowNode childNode = null;
foreach (Statement stmt in statements) {
if (childNode == null) {
childNode = builder.CreateStartNode(stmt);
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 falseEnd;
if (ifElseStatement.FalseStatement.IsNull) {
falseEnd = null;
} else {
ControlFlowNode falseBegin = builder.CreateStartNode(ifElseStatement.FalseStatement);
if (cond != true)
Connect(data, falseBegin, ControlFlowEdgeType.ConditionFalse);
falseEnd = ifElseStatement.FalseStatement.AcceptVisitor(this, falseBegin);
}
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):
ConstantResolveResult 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) {
ConstantResolveResult labelConstant = builder.EvaluateConstant(label.Expression);
if (builder.AreEqualConstants(constant, labelConstant))
sectionMatchedByConstant = section;
}
}
}
if (constant != null && sectionMatchedByConstant == null)
sectionMatchedByConstant = defaultSection;
int gotoCaseOrDefaultInOuterScope = gotoCaseOrDefault.Count;
ControlFlowNode end = builder.CreateEndNode(switchStatement);
breakTargets.Push(end);
foreach (SwitchSection section in switchStatement.SwitchSections) {
if (constant == null || 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.
}
breakTargets.Pop();
if (defaultSection == null && sectionMatchedByConstant == null) {
Connect(data, end);
}
if (gotoCaseOrDefault.Count > gotoCaseOrDefaultInOuterScope) {
// Resolve 'goto case' statements:
throw new NotImplementedException();
}
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)
{
// <data> <condition> while (cond) { <bodyStart> embeddedStmt; <bodyEnd> } <end>
ControlFlowNode end = builder.CreateEndNode(whileStatement);
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();
return end;
}
public override ControlFlowNode VisitDoWhileStatement(DoWhileStatement doWhileStatement, ControlFlowNode data)
{
// <data> do { <bodyStart> embeddedStmt; <bodyEnd>} <condition> while(cond); <end>
ControlFlowNode end = builder.CreateEndNode(doWhileStatement);
ControlFlowNode conditionNode = builder.CreateSpecialNode(doWhileStatement, ControlFlowNodeType.LoopCondition);
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();
return end;
}
public override ControlFlowNode VisitForStatement(ForStatement forStatement, ControlFlowNode data)
{
data = HandleStatementList(forStatement.Initializers, data);
// for (initializers <data>; <condition>cond; <iteratorStart>iterators<iteratorEnd>) { <bodyStart> embeddedStmt; <bodyEnd> } <end>
ControlFlowNode end = builder.CreateEndNode(forStatement);
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);
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)
{
// <data> foreach (<condition>...) { <bodyStart>embeddedStmt<bodyEnd> } <end>
ControlFlowNode end = builder.CreateEndNode(foreachStatement);
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);
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);
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);
}
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 VisitYieldStatement(YieldStatement 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);
}
}
}
}

640
NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs

@ -0,0 +1,640 @@ @@ -0,0 +1,640 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.Utils;
namespace ICSharpCode.NRefactory.CSharp.Analysis
{
/// <summary>
/// Represents the definite assignment status of a variable at a specific location.
/// </summary>
public enum DefiniteAssignmentStatus
{
/// <summary>
/// The variable might be assigned or unassigned.
/// </summary>
PotentiallyAssigned,
/// <summary>
/// The variable is definitely assigned.
/// </summary>
DefinitelyAssigned,
/// <summary>
/// The variable is definitely assigned iff the expression results in the value 'true'.
/// </summary>
AssignedAfterTrueExpression,
/// <summary>
/// The variable is definitely assigned iff the expression results in the value 'false'.
/// </summary>
AssignedAfterFalseExpression,
/// <summary>
/// The code is unreachable.
/// </summary>
CodeUnreachable
}
/// <summary>
/// Implements the C# definite assignment analysis (C# 4.0 Spec: §5.3 Definite assignment)
/// </summary>
public class DefiniteAssignmentAnalysis
{
readonly DefiniteAssignmentVisitor visitor = new DefiniteAssignmentVisitor();
readonly List<ControlFlowNode> allNodes = new List<ControlFlowNode>();
readonly Dictionary<Statement, ControlFlowNode> beginNodeDict = new Dictionary<Statement, ControlFlowNode>();
readonly Dictionary<Statement, ControlFlowNode> endNodeDict = new Dictionary<Statement, ControlFlowNode>();
Dictionary<ControlFlowNode, DefiniteAssignmentStatus> nodeStatus = new Dictionary<ControlFlowNode, DefiniteAssignmentStatus>();
Dictionary<ControlFlowEdge, DefiniteAssignmentStatus> edgeStatus = new Dictionary<ControlFlowEdge, DefiniteAssignmentStatus>();
string variableName;
List<IdentifierExpression> unassignedVariableUses = new List<IdentifierExpression>();
Queue<ControlFlowNode> nodesWithModifiedInput = new Queue<ControlFlowNode>();
public DefiniteAssignmentAnalysis(Statement rootStatement)
{
visitor.analysis = this;
ControlFlowGraphBuilder b = new ControlFlowGraphBuilder();
allNodes.AddRange(b.BuildControlFlowGraph(rootStatement));
foreach (AstNode descendant in rootStatement.Descendants) {
// Anonymous methods have separate control flow graphs, but we also need to analyze those.
AnonymousMethodExpression ame = descendant as AnonymousMethodExpression;
if (ame != null)
allNodes.AddRange(b.BuildControlFlowGraph(ame.Body));
LambdaExpression lambda = descendant as LambdaExpression;
if (lambda != null && lambda.Body is Statement)
allNodes.AddRange(b.BuildControlFlowGraph((Statement)lambda.Body));
}
// Verify that we created nodes for all statements:
Debug.Assert(!rootStatement.DescendantsAndSelf.OfType<Statement>().Except(allNodes.Select(n => n.NextStatement)).Any());
// Now register the nodes in the dictionaries:
foreach (ControlFlowNode node in allNodes) {
if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements)
beginNodeDict.Add(node.NextStatement, node);
if (node.Type == ControlFlowNodeType.BetweenStatements || node.Type == ControlFlowNodeType.EndNode)
endNodeDict.Add(node.PreviousStatement, node);
}
}
/// <summary>
/// Gets the unassigned usages of the previously analyzed variable.
/// </summary>
public IList<IdentifierExpression> UnassignedVariableUses {
get {
return unassignedVariableUses.AsReadOnly();
}
}
public void Analyze(string variable, DefiniteAssignmentStatus initialStatus = DefiniteAssignmentStatus.PotentiallyAssigned)
{
this.variableName = variable;
// Reset the status:
unassignedVariableUses.Clear();
foreach (ControlFlowNode node in allNodes) {
nodeStatus[node] = DefiniteAssignmentStatus.CodeUnreachable;
foreach (ControlFlowEdge edge in node.Outgoing)
edgeStatus[edge] = DefiniteAssignmentStatus.CodeUnreachable;
}
ChangeNodeStatus(allNodes[0], initialStatus);
// Iterate as long as the input status of some nodes is changing:
while (nodesWithModifiedInput.Count > 0) {
ControlFlowNode node = nodesWithModifiedInput.Dequeue();
DefiniteAssignmentStatus inputStatus = DefiniteAssignmentStatus.CodeUnreachable;
foreach (ControlFlowEdge edge in node.Incoming) {
inputStatus = MergeStatus(inputStatus, edgeStatus[edge]);
}
ChangeNodeStatus(node, inputStatus);
}
}
public DefiniteAssignmentStatus GetStatusBefore(Statement statement)
{
return nodeStatus[beginNodeDict[statement]];
}
public DefiniteAssignmentStatus GetStatusAfter(Statement statement)
{
return nodeStatus[endNodeDict[statement]];
}
/// <summary>
/// Exports the CFG. This method is intended to help debugging issues related to definite assignment.
/// </summary>
public GraphVizGraph ExportGraph()
{
GraphVizGraph g = new GraphVizGraph();
g.Title = "DefiniteAssignment - " + variableName;
for (int i = 0; i < allNodes.Count; i++) {
string name = nodeStatus[allNodes[i]].ToString() + Environment.NewLine;
switch (allNodes[i].Type) {
case ControlFlowNodeType.StartNode:
case ControlFlowNodeType.BetweenStatements:
name += allNodes[i].NextStatement.ToString();
break;
case ControlFlowNodeType.EndNode:
name += "End of " + allNodes[i].PreviousStatement.ToString();
break;
case ControlFlowNodeType.LoopCondition:
name += "Condition in " + allNodes[i].NextStatement.ToString();
break;
default:
name += allNodes[i].Type.ToString();
break;
}
g.AddNode(new GraphVizNode(i) { label = name });
foreach (ControlFlowEdge edge in allNodes[i].Outgoing) {
GraphVizEdge ge = new GraphVizEdge(i, allNodes.IndexOf(edge.To));
if (edgeStatus.Count > 0)
ge.label = edgeStatus[edge].ToString();
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;
}
static DefiniteAssignmentStatus MergeStatus(DefiniteAssignmentStatus a, DefiniteAssignmentStatus b)
{
// The result will be DefinitelyAssigned if at least one incoming edge is DefinitelyAssigned and all others are unreachable.
// The result will be DefinitelyUnassigned if at least one incoming edge is DefinitelyUnassigned and all others are unreachable.
// The result will be Unreachable if all incoming edges are unreachable.
// Otherwise, the result will be PotentiallyAssigned.
if (a == b)
return a;
else if (a == DefiniteAssignmentStatus.CodeUnreachable)
return b;
else if (b == DefiniteAssignmentStatus.CodeUnreachable)
return a;
else
return DefiniteAssignmentStatus.PotentiallyAssigned;
}
void ChangeNodeStatus(ControlFlowNode node, DefiniteAssignmentStatus inputStatus)
{
if (nodeStatus[node] == inputStatus)
return;
nodeStatus[node] = inputStatus;
DefiniteAssignmentStatus outputStatus;
switch (node.Type) {
case ControlFlowNodeType.StartNode:
case ControlFlowNodeType.BetweenStatements:
if (node.NextStatement is IfElseStatement) {
// Handle if-else as a condition node
goto case ControlFlowNodeType.LoopCondition;
}
if (inputStatus == DefiniteAssignmentStatus.DefinitelyAssigned) {
// There isn't any way to un-assign variables, so we don't have to check the expression
// if the status already is definitely assigned.
outputStatus = DefiniteAssignmentStatus.DefinitelyAssigned;
} else {
outputStatus = CleanSpecialValues(node.NextStatement.AcceptVisitor(visitor, inputStatus));
}
break;
case ControlFlowNodeType.EndNode:
outputStatus = inputStatus;
if (node.PreviousStatement.Role == TryCatchStatement.FinallyBlockRole
&& (outputStatus == DefiniteAssignmentStatus.DefinitelyAssigned || outputStatus == DefiniteAssignmentStatus.PotentiallyAssigned))
{
TryCatchStatement tryFinally = (TryCatchStatement)node.PreviousStatement.Parent;
// Changing the status on a finally block potentially changes the status of all edges leaving that finally block:
foreach (ControlFlowEdge edge in allNodes.SelectMany(n => n.Outgoing)) {
if (edge.IsLeavingTryFinally && edge.TryFinallyStatements.Contains(tryFinally)) {
DefiniteAssignmentStatus s = edgeStatus[edge];
if (s == DefiniteAssignmentStatus.PotentiallyAssigned) {
ChangeEdgeStatus(edge, outputStatus);
}
}
}
}
break;
case ControlFlowNodeType.LoopCondition:
ForeachStatement foreachStmt = node.NextStatement as ForeachStatement;
if (foreachStmt != null) {
outputStatus = CleanSpecialValues(foreachStmt.InExpression.AcceptVisitor(visitor, inputStatus));
if (foreachStmt.VariableName == this.variableName)
outputStatus = DefiniteAssignmentStatus.DefinitelyAssigned;
break;
} else {
Debug.Assert(node.NextStatement is IfElseStatement || node.NextStatement is WhileStatement || node.NextStatement is ForStatement || node.NextStatement is DoWhileStatement);
Expression condition = node.NextStatement.GetChildByRole(AstNode.Roles.Condition);
if (condition.IsNull)
outputStatus = inputStatus;
else
outputStatus = condition.AcceptVisitor(visitor, inputStatus);
foreach (ControlFlowEdge edge in node.Outgoing) {
if (edge.Type == ControlFlowEdgeType.ConditionTrue && outputStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression) {
ChangeEdgeStatus(edge, DefiniteAssignmentStatus.DefinitelyAssigned);
} else if (edge.Type == ControlFlowEdgeType.ConditionFalse && outputStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression) {
ChangeEdgeStatus(edge, DefiniteAssignmentStatus.DefinitelyAssigned);
} else {
ChangeEdgeStatus(edge, CleanSpecialValues(outputStatus));
}
}
return;
}
default:
throw new InvalidOperationException();
}
foreach (ControlFlowEdge edge in node.Outgoing) {
ChangeEdgeStatus(edge, outputStatus);
}
}
void ChangeEdgeStatus(ControlFlowEdge edge, DefiniteAssignmentStatus newStatus)
{
DefiniteAssignmentStatus oldStatus = edgeStatus[edge];
if (oldStatus == newStatus)
return;
// Ensure that status can change only in one direction:
// CodeUnreachable -> PotentiallyAssigned -> DefinitelyAssigned
// Going against this direction indicates a bug and could cause infinite loops.
switch (oldStatus) {
case DefiniteAssignmentStatus.PotentiallyAssigned:
if (newStatus != DefiniteAssignmentStatus.DefinitelyAssigned)
throw new InvalidOperationException("Invalid state transition");
break;
case DefiniteAssignmentStatus.CodeUnreachable:
if (!(newStatus == DefiniteAssignmentStatus.PotentiallyAssigned || newStatus == DefiniteAssignmentStatus.DefinitelyAssigned))
throw new InvalidOperationException("Invalid state transition");
break;
case DefiniteAssignmentStatus.DefinitelyAssigned:
throw new InvalidOperationException("Invalid state transition");
default:
throw new InvalidOperationException("Invalid value for DefiniteAssignmentStatus");
}
edgeStatus[edge] = newStatus;
nodesWithModifiedInput.Enqueue(edge.To);
}
/// <summary>
/// Evaluates an expression.
/// </summary>
/// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
ConstantResolveResult EvaluateConstant(Expression expr)
{
return null; // TODO: implement this using the C# resolver
}
/// <summary>
/// Evaluates an expression.
/// </summary>
/// <returns>The value of the constant boolean expression; or null if the value is not a constant boolean expression.</returns>
bool? EvaluateCondition(Expression expr)
{
ConstantResolveResult crr = EvaluateConstant(expr);
if (crr != null)
return crr.ConstantValue as bool?;
else
return null;
}
static DefiniteAssignmentStatus CleanSpecialValues(DefiniteAssignmentStatus status)
{
if (status == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
return DefiniteAssignmentStatus.PotentiallyAssigned;
else if (status == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
return DefiniteAssignmentStatus.PotentiallyAssigned;
else
return status;
}
sealed class DefiniteAssignmentVisitor : DepthFirstAstVisitor<DefiniteAssignmentStatus, DefiniteAssignmentStatus>
{
internal DefiniteAssignmentAnalysis analysis;
// The general approach for unknown nodes is to pass the status through all child nodes in order
protected override DefiniteAssignmentStatus VisitChildren(AstNode node, DefiniteAssignmentStatus data)
{
// the special values are valid as output only, not as input
Debug.Assert(data == CleanSpecialValues(data));
DefiniteAssignmentStatus status = data;
foreach (AstNode child in node.Children) {
Debug.Assert(!(child is Statement)); // statements are visited with the CFG, not with the visitor pattern
status = child.AcceptVisitor(this, status);
status = CleanSpecialValues(status);
}
return status;
}
#region Statements
// For statements, the visitor only describes the effect of the statement itself;
// we do not consider the effect of any nested statements.
// This is done because the nested statements will be reached using the control flow graph.
// In fact, these methods are present so that the default logic in VisitChildren does not try to visit the nested statements.
public override DefiniteAssignmentStatus VisitBlockStatement(BlockStatement blockStatement, DefiniteAssignmentStatus data)
{
return data;
}
public override DefiniteAssignmentStatus VisitCheckedStatement(CheckedStatement checkedStatement, DefiniteAssignmentStatus data)
{
return data;
}
public override DefiniteAssignmentStatus VisitUncheckedStatement(UncheckedStatement uncheckedStatement, DefiniteAssignmentStatus data)
{
return data;
}
// ExpressionStatement handled by default logic
// VariableDeclarationStatement handled by default logic
public override DefiniteAssignmentStatus VisitVariableInitializer(VariableInitializer variableInitializer, DefiniteAssignmentStatus data)
{
if (variableInitializer.Initializer.IsNull) {
return data;
} else {
DefiniteAssignmentStatus status = variableInitializer.Initializer.AcceptVisitor(this, data);
if (variableInitializer.Name == analysis.variableName)
return DefiniteAssignmentStatus.DefinitelyAssigned;
else
return status;
}
}
// IfStatement not handled by visitor, but special-cased in the code consuming the control flow graph
public override DefiniteAssignmentStatus VisitSwitchStatement(SwitchStatement switchStatement, DefiniteAssignmentStatus data)
{
return switchStatement.Expression.AcceptVisitor(this, data);
}
public override DefiniteAssignmentStatus VisitWhileStatement(WhileStatement whileStatement, DefiniteAssignmentStatus data)
{
return data; // condition is handled by special condition CFG node
}
public override DefiniteAssignmentStatus VisitDoWhileStatement(DoWhileStatement doWhileStatement, DefiniteAssignmentStatus data)
{
return data; // condition is handled by special condition CFG node
}
public override DefiniteAssignmentStatus VisitForStatement(ForStatement forStatement, DefiniteAssignmentStatus data)
{
return data; // condition is handled by special condition CFG node; initializer and iterator statements are handled by CFG
}
// Break/Continue/Goto: handled by default logic
// ThrowStatement: handled by default logic (just visit the expression)
// ReturnStatement: handled by default logic (just visit the expression)
public override DefiniteAssignmentStatus VisitTryCatchStatement(TryCatchStatement tryCatchStatement, DefiniteAssignmentStatus data)
{
return data; // no special logic when entering the try-catch-finally statement
// TODO: where to put the special logic when exiting the try-finally statement?
}
public override DefiniteAssignmentStatus VisitForeachStatement(ForeachStatement foreachStatement, DefiniteAssignmentStatus data)
{
return data; // assignment of the foreach loop variable is done when handling the condition node
}
public override DefiniteAssignmentStatus VisitUsingStatement(UsingStatement usingStatement, DefiniteAssignmentStatus data)
{
if (usingStatement.ResourceAcquisition is Expression)
return usingStatement.ResourceAcquisition.AcceptVisitor(this, data);
else
return data; // don't handle resource acquisition statements, as those are connected in the control flow graph
}
public override DefiniteAssignmentStatus VisitLockStatement(LockStatement lockStatement, DefiniteAssignmentStatus data)
{
return lockStatement.Expression.AcceptVisitor(this, data);
}
// Yield statements use the default logic
public override DefiniteAssignmentStatus VisitUnsafeStatement(UnsafeStatement unsafeStatement, DefiniteAssignmentStatus data)
{
return data;
}
public override DefiniteAssignmentStatus VisitFixedStatement(FixedStatement fixedStatement, DefiniteAssignmentStatus data)
{
DefiniteAssignmentStatus status = data;
foreach (var variable in fixedStatement.Variables)
status = variable.AcceptVisitor(this, status);
return status;
}
#endregion
public override DefiniteAssignmentStatus VisitDirectionExpression(DirectionExpression directionExpression, DefiniteAssignmentStatus data)
{
if (directionExpression.FieldDirection == FieldDirection.Out) {
return HandleAssignment(directionExpression.Expression, null, data);
} else {
// use default logic for 'ref'
return VisitChildren(directionExpression, data);
}
}
public override DefiniteAssignmentStatus VisitAssignmentExpression(AssignmentExpression assignmentExpression, DefiniteAssignmentStatus data)
{
if (assignmentExpression.Operator == AssignmentOperatorType.Assign) {
return HandleAssignment(assignmentExpression.Left, assignmentExpression.Right, data);
} else {
// use default logic for compound assignment operators
return VisitChildren(assignmentExpression, data);
}
}
DefiniteAssignmentStatus HandleAssignment(Expression left, Expression right, DefiniteAssignmentStatus initialStatus)
{
IdentifierExpression ident = left as IdentifierExpression;
if (ident != null && ident.Identifier == analysis.variableName) {
right.AcceptVisitor(this, initialStatus);
return DefiniteAssignmentStatus.DefinitelyAssigned;
} else {
DefiniteAssignmentStatus status = left.AcceptVisitor(this, initialStatus);
status = right.AcceptVisitor(this, CleanSpecialValues(status));
return CleanSpecialValues(status);
}
}
public override DefiniteAssignmentStatus VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, DefiniteAssignmentStatus data)
{
// Don't use the default logic here because we don't want to clean up the special values.
return parenthesizedExpression.Expression.AcceptVisitor(this, data);
}
public override DefiniteAssignmentStatus VisitCheckedExpression(CheckedExpression checkedExpression, DefiniteAssignmentStatus data)
{
return checkedExpression.Expression.AcceptVisitor(this, data);
}
public override DefiniteAssignmentStatus VisitUncheckedExpression(UncheckedExpression uncheckedExpression, DefiniteAssignmentStatus data)
{
return uncheckedExpression.Expression.AcceptVisitor(this, data);
}
public override DefiniteAssignmentStatus VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, DefiniteAssignmentStatus data)
{
if (binaryOperatorExpression.Operator == BinaryOperatorType.ConditionalAnd) {
// Handle constant left side of && expressions (not in the C# spec, but done by the MS compiler)
bool? cond = analysis.EvaluateCondition(binaryOperatorExpression.Left);
if (cond == true)
return binaryOperatorExpression.Right.AcceptVisitor(this, data); // right operand gets evaluated unconditionally
else if (cond == false)
return data; // right operand never gets evaluated
// C# 4.0 spec: §5.3.3.24 Definite Assignment for && expressions
DefiniteAssignmentStatus afterLeft = binaryOperatorExpression.Left.AcceptVisitor(this, data);
DefiniteAssignmentStatus beforeRight;
if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
beforeRight = DefiniteAssignmentStatus.DefinitelyAssigned;
else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
beforeRight = DefiniteAssignmentStatus.PotentiallyAssigned;
else
beforeRight = afterLeft;
DefiniteAssignmentStatus afterRight = binaryOperatorExpression.Right.AcceptVisitor(this, beforeRight);
if (afterLeft == DefiniteAssignmentStatus.DefinitelyAssigned)
return DefiniteAssignmentStatus.DefinitelyAssigned;
else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned && afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
return DefiniteAssignmentStatus.DefinitelyAssigned;
else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned || afterRight == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
return DefiniteAssignmentStatus.AssignedAfterTrueExpression;
else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression && afterRight == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
return DefiniteAssignmentStatus.AssignedAfterFalseExpression;
else
return DefiniteAssignmentStatus.PotentiallyAssigned;
} else if (binaryOperatorExpression.Operator == BinaryOperatorType.ConditionalOr) {
// C# 4.0 spec: §5.3.3.25 Definite Assignment for || expressions
bool? cond = analysis.EvaluateCondition(binaryOperatorExpression.Left);
if (cond == false)
return binaryOperatorExpression.Right.AcceptVisitor(this, data); // right operand gets evaluated unconditionally
else if (cond == true)
return data; // right operand never gets evaluated
DefiniteAssignmentStatus afterLeft = binaryOperatorExpression.Left.AcceptVisitor(this, data);
DefiniteAssignmentStatus beforeRight;
if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
beforeRight = DefiniteAssignmentStatus.PotentiallyAssigned;
else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
beforeRight = DefiniteAssignmentStatus.DefinitelyAssigned;
else
beforeRight = afterLeft;
DefiniteAssignmentStatus afterRight = binaryOperatorExpression.Right.AcceptVisitor(this, beforeRight);
if (afterLeft == DefiniteAssignmentStatus.DefinitelyAssigned)
return DefiniteAssignmentStatus.DefinitelyAssigned;
else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned && afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
return DefiniteAssignmentStatus.DefinitelyAssigned;
else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned || afterRight == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
return DefiniteAssignmentStatus.AssignedAfterFalseExpression;
else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression && afterRight == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
return DefiniteAssignmentStatus.AssignedAfterTrueExpression;
else
return DefiniteAssignmentStatus.PotentiallyAssigned;
} else if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) {
// C# 4.0 spec: §5.3.3.27 Definite assignment for ?? expressions
ConstantResolveResult crr = analysis.EvaluateConstant(binaryOperatorExpression.Left);
if (crr != null && crr.ConstantValue == null)
return binaryOperatorExpression.Right.AcceptVisitor(this, data);
DefiniteAssignmentStatus status = CleanSpecialValues(binaryOperatorExpression.Left.AcceptVisitor(this, data));
binaryOperatorExpression.Right.AcceptVisitor(this, status);
return status;
} else {
// use default logic for other operators
return VisitChildren(binaryOperatorExpression, data);
}
}
public override DefiniteAssignmentStatus VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, DefiniteAssignmentStatus data)
{
if (unaryOperatorExpression.Operator == UnaryOperatorType.Not) {
// C# 4.0 spec: §5.3.3.26 Definite assignment for ! expressions
DefiniteAssignmentStatus status = unaryOperatorExpression.Expression.AcceptVisitor(this, data);
if (status == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
return DefiniteAssignmentStatus.AssignedAfterTrueExpression;
else if (status == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
return DefiniteAssignmentStatus.AssignedAfterFalseExpression;
else
return status;
} else {
// use default logic for other operators
return VisitChildren(unaryOperatorExpression, data);
}
}
public override DefiniteAssignmentStatus VisitConditionalExpression(ConditionalExpression conditionalExpression, DefiniteAssignmentStatus data)
{
// C# 4.0 spec: §5.3.3.28 Definite assignment for ?: expressions
bool? cond = analysis.EvaluateCondition(conditionalExpression.Condition);
if (cond == true) {
return conditionalExpression.TrueExpression.AcceptVisitor(this, data);
} else if (cond == false) {
return conditionalExpression.FalseExpression.AcceptVisitor(this, data);
} else {
DefiniteAssignmentStatus afterCondition = conditionalExpression.Condition.AcceptVisitor(this, data);
DefiniteAssignmentStatus beforeTrue, beforeFalse;
if (afterCondition == DefiniteAssignmentStatus.AssignedAfterTrueExpression) {
beforeTrue = DefiniteAssignmentStatus.DefinitelyAssigned;
beforeFalse = DefiniteAssignmentStatus.PotentiallyAssigned;
} else if (afterCondition == DefiniteAssignmentStatus.AssignedAfterFalseExpression) {
beforeTrue = DefiniteAssignmentStatus.PotentiallyAssigned;
beforeFalse = DefiniteAssignmentStatus.DefinitelyAssigned;
} else {
beforeTrue = afterCondition;
beforeFalse = afterCondition;
}
DefiniteAssignmentStatus afterTrue = conditionalExpression.TrueExpression.AcceptVisitor(this, beforeTrue);
DefiniteAssignmentStatus afterFalse = conditionalExpression.TrueExpression.AcceptVisitor(this, beforeFalse);
return MergeStatus(CleanSpecialValues(afterTrue), CleanSpecialValues(afterFalse));
}
}
public override DefiniteAssignmentStatus VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, DefiniteAssignmentStatus data)
{
BlockStatement body = anonymousMethodExpression.Body;
foreach (ControlFlowNode node in analysis.allNodes) {
if (node.NextStatement == body)
analysis.ChangeNodeStatus(node, data);
}
return data;
}
public override DefiniteAssignmentStatus VisitLambdaExpression(LambdaExpression lambdaExpression, DefiniteAssignmentStatus data)
{
Statement body = lambdaExpression.Body as Statement;
if (body != null) {
foreach (ControlFlowNode node in analysis.allNodes) {
if (node.NextStatement == body)
analysis.ChangeNodeStatus(node, data);
}
} else {
lambdaExpression.Body.AcceptVisitor(this, data);
}
return data;
}
public override DefiniteAssignmentStatus VisitIdentifierExpression(IdentifierExpression identifierExpression, DefiniteAssignmentStatus data)
{
if (data != DefiniteAssignmentStatus.DefinitelyAssigned
&& identifierExpression.Identifier == analysis.variableName && identifierExpression.TypeArguments.Count == 0)
{
analysis.unassignedVariableUses.Add(identifierExpression);
}
return data;
}
}
}
}

7
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs

@ -30,10 +30,11 @@ namespace ICSharpCode.NRefactory.CSharp @@ -30,10 +30,11 @@ namespace ICSharpCode.NRefactory.CSharp
public int Count {
get {
var e = GetEnumerator();
int count = 0;
while (e.MoveNext())
count++;
for (AstNode cur = node.FirstChild; cur != null; cur = cur.NextSibling) {
if (cur.Role == role)
count++;
}
return count;
}
}

8
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/DepthFirstAstVisitor.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
//
//
// IAstVisitor.cs
//
// Author:
@ -52,17 +52,17 @@ namespace ICSharpCode.NRefactory.CSharp @@ -52,17 +52,17 @@ namespace ICSharpCode.NRefactory.CSharp
public virtual S VisitComment (Comment comment, T data)
{
return default (S);
return VisitChildren (comment, data);
}
public virtual S VisitIdentifier (Identifier identifier, T data)
{
return default (S);
return VisitChildren (identifier, data);
}
public virtual S VisitCSharpTokenNode (CSharpTokenNode token, T data)
{
return default (S);
return VisitChildren (token, data);
}
public virtual S VisitPrimitiveType (PrimitiveType primitiveType, T data)

2
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/Expression.cs

@ -54,6 +54,8 @@ namespace ICSharpCode.NRefactory.CSharp @@ -54,6 +54,8 @@ namespace ICSharpCode.NRefactory.CSharp
// Make debugging easier by giving Expressions a ToString() implementation
public override string ToString()
{
if (IsNull)
return "Null";
StringWriter w = new StringWriter();
AcceptVisitor(new OutputVisitor(w, new CSharpFormattingPolicy()), null);
return w.ToString();

15
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/Statement.cs

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.IO;
namespace ICSharpCode.NRefactory.CSharp
{
@ -52,5 +53,19 @@ namespace ICSharpCode.NRefactory.CSharp @@ -52,5 +53,19 @@ namespace ICSharpCode.NRefactory.CSharp
public override NodeType NodeType {
get { return NodeType.Statement; }
}
// Make debugging easier by giving Statements a ToString() implementation
public override string ToString()
{
if (IsNull)
return "Null";
StringWriter w = new StringWriter();
AcceptVisitor(new OutputVisitor(w, new CSharpFormattingPolicy()), null);
string text = w.ToString().TrimEnd().Replace("\t", "").Replace(w.NewLine, " ");
if (text.Length > 100)
return text.Substring(0, 97) + "...";
else
return text;
}
}
}

6
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/Accessor.cs

@ -56,11 +56,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -56,11 +56,7 @@ namespace ICSharpCode.NRefactory.CSharp
get { return GetChildByRole (Roles.Body); }
set { SetChildByRole (Roles.Body, value); }
}
public AstNodeCollection<ParameterDeclaration> Parameters {
get { return GetChildrenByRole(Roles.Parameter); }
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return visitor.VisitAccessor (this, data);

14
NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
<OutputType>Library</OutputType>
<RootNamespace>ICSharpCode.NRefactory</RootNamespace>
<AssemblyName>ICSharpCode.NRefactory</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<AppDesignerFolder>Properties</AppDesignerFolder>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
<NoStdLib>False</NoStdLib>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<NoWarn>1591,0618</NoWarn>
<TargetFrameworkProfile />
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<RunCodeAnalysis>False</RunCodeAnalysis>
<CodeAnalysisRules>-Microsoft.Design#CA1026;-Microsoft.Security#CA2104</CodeAnalysisRules>
</PropertyGroup>
@ -30,14 +30,14 @@ @@ -30,14 +30,14 @@
<OutputPath>bin\Debug\</OutputPath>
<DebugType>Full</DebugType>
<Optimize>False</Optimize>
<DefineConstants>DEBUG;TRACE;FULL_AST;DOTNET35</DefineConstants>
<DefineConstants>DEBUG;TRACE;FULL_AST</DefineConstants>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>bin\Release\</OutputPath>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
<DefineConstants>TRACE;FULL_AST;DOTNET35</DefineConstants>
<DefineConstants>TRACE;FULL_AST</DefineConstants>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@ -61,6 +61,8 @@ @@ -61,6 +61,8 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CSharp\Analysis\ControlFlow.cs" />
<Compile Include="CSharp\Analysis\DefiniteAssignmentAnalysis.cs" />
<Compile Include="CSharp\Ast\AstNodeCollection.cs" />
<Compile Include="CSharp\Ast\Expressions\TypeReferenceExpression.cs" />
<Compile Include="CSharp\Ast\IAstVisitor.cs" />
@ -334,6 +336,7 @@ @@ -334,6 +336,7 @@
<Compile Include="Utils\DotNet35Compat.cs" />
<Compile Include="Utils\EmptyList.cs" />
<Compile Include="Utils\ExtensionMethods.cs" />
<Compile Include="Utils\GraphVizGraph.cs" />
<Compile Include="Utils\TreeTraversal.cs" />
<Compile Include="CSharp\Ast\ComposedType.cs" />
<Compile Include="CSharp\Ast\Expressions\DirectionExpression.cs" />
@ -356,5 +359,8 @@ @@ -356,5 +359,8 @@
<Name>Mono.Cecil</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="CSharp\Analysis" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

4
NRefactory/ICSharpCode.NRefactory/Utils/DotNet35Compat.cs

@ -16,7 +16,7 @@ internal static class DotNet35Compat @@ -16,7 +16,7 @@ internal static class DotNet35Compat
#endif
}
public static IEnumerable<U> SafeCast<T, U>(this IEnumerable<T> elements) where T : U
public static IEnumerable<U> SafeCast<T, U>(this IEnumerable<T> elements) where T : class, U where U : class
{
#if DOTNET35
foreach (T item in elements)
@ -26,7 +26,7 @@ internal static class DotNet35Compat @@ -26,7 +26,7 @@ internal static class DotNet35Compat
#endif
}
public static Predicate<U> SafeCast<T, U>(this Predicate<T> predicate) where U : T
public static Predicate<U> SafeCast<T, U>(this Predicate<T> predicate) where U : class, T where T : class
{
#if DOTNET35
return e => predicate(e);

43
ICSharpCode.Decompiler/GraphVizGraph.cs → NRefactory/ICSharpCode.NRefactory/Utils/GraphVizGraph.cs

@ -1,28 +1,14 @@ @@ -1,28 +1,14 @@
// Copyright (c) 2011 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.
// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
namespace ICSharpCode.Decompiler
namespace ICSharpCode.NRefactory.Utils
{
/// <summary>
/// GraphViz graph.
@ -33,6 +19,7 @@ namespace ICSharpCode.Decompiler @@ -33,6 +19,7 @@ namespace ICSharpCode.Decompiler
List<GraphVizEdge> edges = new List<GraphVizEdge>();
public string rankdir;
public string Title;
public void AddEdge(GraphVizEdge edge)
{
@ -50,6 +37,24 @@ namespace ICSharpCode.Decompiler @@ -50,6 +37,24 @@ namespace ICSharpCode.Decompiler
Save(writer);
}
public void Show()
{
Show(null);
}
public void Show(string name)
{
if (name == null)
name = Title;
if (name != null)
foreach (char c in Path.GetInvalidFileNameChars())
name = name.Replace(c, '-');
string fileName = name != null ? Path.Combine(Path.GetTempPath(), name) : Path.GetTempFileName();
Save(fileName + ".gv");
Process.Start("dot", "\"" + fileName + ".gv\" -Tpng -o \"" + fileName + ".png\"").WaitForExit();
Process.Start(fileName + ".png");
}
static string Escape(string text)
{
if (Regex.IsMatch(text, @"^[\w\d]+$")) {
@ -92,6 +97,8 @@ namespace ICSharpCode.Decompiler @@ -92,6 +97,8 @@ namespace ICSharpCode.Decompiler
public void Save(TextWriter writer)
{
if (writer == null)
throw new ArgumentNullException("writer");
writer.WriteLine("digraph G {");
writer.WriteLine("node [fontsize = 16];");
WriteGraphAttribute(writer, "rankdir", rankdir);
Loading…
Cancel
Save