Browse Source

Order the control flow nodes lexically, and allow restricting definite assignment analysis to a specific lexical range.

pull/100/head
Daniel Grunwald 15 years ago
parent
commit
a2fb74bee6
  1. 36
      NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs
  2. 151
      NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs

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

@ -222,14 +222,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -222,14 +222,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
return node;
}
ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type)
ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type, bool addToNodeList = true)
{
ControlFlowNode node = CreateNode(null, statement, type);
nodes.Add(node);
if (addToNodeList)
nodes.Add(node);
return node;
}
ControlFlowNode CreateEndNode(Statement statement)
ControlFlowNode CreateEndNode(Statement statement, bool addToNodeList = true)
{
Statement nextStatement;
if (statement == rootStatement) {
@ -244,7 +245,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -244,7 +245,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
}
ControlFlowNodeType type = nextStatement != null ? ControlFlowNodeType.BetweenStatements : ControlFlowNodeType.EndNode;
ControlFlowNode node = CreateNode(statement, nextStatement, type);
nodes.Add(node);
if (addToNodeList)
nodes.Add(node);
return node;
}
#endregion
@ -259,7 +261,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -259,7 +261,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// Evaluates an expression.
/// </summary>
/// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
internal ConstantResolveResult EvaluateConstant(Expression expr)
ConstantResolveResult EvaluateConstant(Expression expr)
{
if (EvaluateOnlyPrimitiveConstants) {
if (!(expr is PrimitiveExpression || expr is NullReferenceExpression))
@ -272,7 +274,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -272,7 +274,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// Evaluates an expression.
/// </summary>
/// <returns>The value of the constant boolean expression; or null if the value is not a constant boolean expression.</returns>
internal bool? EvaluateCondition(Expression expr)
bool? EvaluateCondition(Expression expr)
{
ConstantResolveResult rr = EvaluateConstant(expr);
if (rr != null)
@ -418,7 +420,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -418,7 +420,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
int gotoCaseOrDefaultInOuterScope = gotoCaseOrDefault.Count;
ControlFlowNode end = builder.CreateEndNode(switchStatement);
ControlFlowNode end = builder.CreateEndNode(switchStatement, addToNodeList: false);
breakTargets.Push(end);
foreach (SwitchSection section in switchStatement.SwitchSections) {
if (constant == null || section == sectionMatchedByConstant) {
@ -439,6 +441,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -439,6 +441,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
throw new NotImplementedException();
}
builder.nodes.Add(end);
return end;
}
@ -457,7 +460,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -457,7 +460,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override ControlFlowNode VisitWhileStatement(WhileStatement whileStatement, ControlFlowNode data)
{
// <data> <condition> while (cond) { <bodyStart> embeddedStmt; <bodyEnd> } <end>
ControlFlowNode end = builder.CreateEndNode(whileStatement);
ControlFlowNode end = builder.CreateEndNode(whileStatement, addToNodeList: false);
ControlFlowNode conditionNode = builder.CreateSpecialNode(whileStatement, ControlFlowNodeType.LoopCondition);
breakTargets.Push(end);
continueTargets.Push(conditionNode);
@ -475,14 +478,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -475,14 +478,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
breakTargets.Pop();
continueTargets.Pop();
builder.nodes.Add(end);
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);
ControlFlowNode end = builder.CreateEndNode(doWhileStatement, addToNodeList: false);
ControlFlowNode conditionNode = builder.CreateSpecialNode(doWhileStatement, ControlFlowNodeType.LoopCondition, addToNodeList: false);
breakTargets.Push(end);
continueTargets.Push(conditionNode);
@ -499,6 +503,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -499,6 +503,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
breakTargets.Pop();
continueTargets.Pop();
builder.nodes.Add(conditionNode);
builder.nodes.Add(end);
return end;
}
@ -506,7 +512,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -506,7 +512,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
{
data = HandleStatementList(forStatement.Initializers, data);
// for (initializers <data>; <condition>cond; <iteratorStart>iterators<iteratorEnd>) { <bodyStart> embeddedStmt; <bodyEnd> } <end>
ControlFlowNode end = builder.CreateEndNode(forStatement);
ControlFlowNode end = builder.CreateEndNode(forStatement, addToNodeList: false);
ControlFlowNode conditionNode = builder.CreateSpecialNode(forStatement, ControlFlowNodeType.LoopCondition);
Connect(data, conditionNode);
@ -536,6 +542,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -536,6 +542,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
if (cond != true)
Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse);
builder.nodes.Add(end);
return end;
}
@ -552,7 +559,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -552,7 +559,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override ControlFlowNode VisitForeachStatement(ForeachStatement foreachStatement, ControlFlowNode data)
{
// <data> foreach (<condition>...) { <bodyStart>embeddedStmt<bodyEnd> } <end>
ControlFlowNode end = builder.CreateEndNode(foreachStatement);
ControlFlowNode end = builder.CreateEndNode(foreachStatement, addToNodeList: false);
ControlFlowNode conditionNode = builder.CreateSpecialNode(foreachStatement, ControlFlowNodeType.LoopCondition);
Connect(data, conditionNode);
@ -566,7 +573,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -566,7 +573,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
continueTargets.Pop();
Connect(conditionNode, end);
builder.nodes.Add(end);
return end;
}
@ -602,7 +609,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -602,7 +609,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override ControlFlowNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement, ControlFlowNode data)
{
ControlFlowNode end = builder.CreateEndNode(tryCatchStatement);
ControlFlowNode end = builder.CreateEndNode(tryCatchStatement, addToNodeList: false);
var edge = Connect(HandleEmbeddedStatement(tryCatchStatement.TryBlock, data), end);
if (!tryCatchStatement.FinallyBlock.IsNull)
edge.AddJumpOutOfTryFinally(tryCatchStatement);
@ -616,6 +623,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -616,6 +623,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
// Consumers of the CFG will have to special-case try-finally.
HandleEmbeddedStatement(tryCatchStatement.FinallyBlock, data);
}
builder.nodes.Add(end);
return end;
}

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

@ -44,21 +44,40 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -44,21 +44,40 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// </summary>
public class DefiniteAssignmentAnalysis
{
readonly ControlFlowGraphBuilder cfgBuilder = new ControlFlowGraphBuilder();
sealed class DefiniteAssignmentNode : ControlFlowNode
{
public int Index;
public DefiniteAssignmentStatus NodeStatus;
public DefiniteAssignmentNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type)
: base(previousStatement, nextStatement, type)
{
}
}
sealed class DerivedControlFlowGraphBuilder : ControlFlowGraphBuilder
{
protected override ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type)
{
return new DefiniteAssignmentNode(previousStatement, nextStatement, type);
}
}
readonly DerivedControlFlowGraphBuilder cfgBuilder = new DerivedControlFlowGraphBuilder();
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>();
readonly Dictionary<Statement, ControlFlowNode> conditionNodeDict = new Dictionary<Statement, ControlFlowNode>();
readonly List<DefiniteAssignmentNode> allNodes = new List<DefiniteAssignmentNode>();
readonly Dictionary<Statement, DefiniteAssignmentNode> beginNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>();
readonly Dictionary<Statement, DefiniteAssignmentNode> endNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>();
readonly Dictionary<Statement, DefiniteAssignmentNode> conditionNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>();
readonly ResolveVisitor resolveVisitor;
readonly CancellationToken cancellationToken;
Dictionary<ControlFlowNode, DefiniteAssignmentStatus> nodeStatus = new Dictionary<ControlFlowNode, DefiniteAssignmentStatus>();
Dictionary<ControlFlowEdge, DefiniteAssignmentStatus> edgeStatus = new Dictionary<ControlFlowEdge, DefiniteAssignmentStatus>();
string variableName;
List<IdentifierExpression> unassignedVariableUses = new List<IdentifierExpression>();
int analyzedRangeStart, analyzedRangeEnd;
Queue<ControlFlowNode> nodesWithModifiedInput = new Queue<ControlFlowNode>();
Queue<DefiniteAssignmentNode> nodesWithModifiedInput = new Queue<DefiniteAssignmentNode>();
public DefiniteAssignmentAnalysis(Statement rootStatement, CancellationToken cancellationToken = default(CancellationToken))
: this(rootStatement, null, cancellationToken)
@ -83,20 +102,18 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -83,20 +102,18 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
if (resolveVisitor.TypeResolveContext is MinimalResolveContext) {
cfgBuilder.EvaluateOnlyPrimitiveConstants = true;
}
allNodes.AddRange(cfgBuilder.BuildControlFlowGraph(rootStatement, resolveVisitor));
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(cfgBuilder.BuildControlFlowGraph(ame.Body, resolveVisitor));
LambdaExpression lambda = descendant as LambdaExpression;
if (lambda != null && lambda.Body is Statement)
allNodes.AddRange(cfgBuilder.BuildControlFlowGraph((Statement)lambda.Body, resolveVisitor));
}
// 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) {
allNodes.AddRange(cfgBuilder.BuildControlFlowGraph(rootStatement, resolveVisitor).Cast<DefiniteAssignmentNode>());
for (int i = 0; i < allNodes.Count; i++) {
DefiniteAssignmentNode node = allNodes[i];
node.Index = i; // assign numbers to the nodes
if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements) {
// Anonymous methods have separate control flow graphs, but we also need to analyze those.
// Iterate backwards so that anonymous methods are inserted in the correct order
for (AstNode child = node.NextStatement.LastChild; child != null; child = child.PrevSibling) {
InsertAnonymousMethods(i + 1, child);
}
}
// Now register the node in the dictionaries:
if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements)
beginNodeDict.Add(node.NextStatement, node);
if (node.Type == ControlFlowNodeType.BetweenStatements || node.Type == ControlFlowNodeType.EndNode)
@ -104,6 +121,32 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -104,6 +121,32 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
if (node.Type == ControlFlowNodeType.LoopCondition)
conditionNodeDict.Add(node.NextStatement, node);
}
// Verify that we created nodes for all statements:
Debug.Assert(!rootStatement.DescendantsAndSelf.OfType<Statement>().Except(allNodes.Select(n => n.NextStatement)).Any());
this.analyzedRangeStart = 0;
this.analyzedRangeEnd = allNodes.Count - 1;
}
void InsertAnonymousMethods(int insertPos, AstNode node)
{
// Ignore any statements, as those have their own ControlFlowNode and get handled separately
if (node is Statement)
return;
AnonymousMethodExpression ame = node as AnonymousMethodExpression;
if (ame != null) {
allNodes.InsertRange(insertPos, cfgBuilder.BuildControlFlowGraph(ame.Body, resolveVisitor).Cast<DefiniteAssignmentNode>());
return;
}
LambdaExpression lambda = node as LambdaExpression;
if (lambda != null && lambda.Body is Statement) {
allNodes.InsertRange(insertPos, cfgBuilder.BuildControlFlowGraph((Statement)lambda.Body, resolveVisitor).Cast<DefiniteAssignmentNode>());
return;
}
// Descend into child expressions
// Iterate backwards so that anonymous methods are inserted in the correct order
for (AstNode child = node.LastChild; child != null; child = child.PrevSibling) {
InsertAnonymousMethods(insertPos, child);
}
}
/// <summary>
@ -115,21 +158,37 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -115,21 +158,37 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
}
}
/// <summary>
/// Sets the range of statements to be analyzed.
/// This method can be used to restrict the analysis to only a part of the method.
/// Only the control flow paths that are fully contained within the selected part will be analyzed.
/// </summary>
/// <remarks>Both 'start' and 'end' are inclusive.</remarks>
public void SetAnalyzedRange(Statement start, Statement end)
{
int startIndex = beginNodeDict[start].Index;
int endIndex = endNodeDict[end].Index;
if (startIndex > endIndex)
throw new ArgumentException("The start statement must be lexically preceding the end statement");
this.analyzedRangeStart = startIndex;
this.analyzedRangeEnd = endIndex;
}
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 (DefiniteAssignmentNode node in allNodes) {
node.NodeStatus = DefiniteAssignmentStatus.CodeUnreachable;
foreach (ControlFlowEdge edge in node.Outgoing)
edgeStatus[edge] = DefiniteAssignmentStatus.CodeUnreachable;
}
ChangeNodeStatus(allNodes[0], initialStatus);
ChangeNodeStatus(allNodes[analyzedRangeStart], initialStatus);
// Iterate as long as the input status of some nodes is changing:
while (nodesWithModifiedInput.Count > 0) {
ControlFlowNode node = nodesWithModifiedInput.Dequeue();
DefiniteAssignmentNode node = nodesWithModifiedInput.Dequeue();
DefiniteAssignmentStatus inputStatus = DefiniteAssignmentStatus.CodeUnreachable;
foreach (ControlFlowEdge edge in node.Incoming) {
inputStatus = MergeStatus(inputStatus, edgeStatus[edge]);
@ -140,17 +199,17 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -140,17 +199,17 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public DefiniteAssignmentStatus GetStatusBefore(Statement statement)
{
return nodeStatus[beginNodeDict[statement]];
return beginNodeDict[statement].NodeStatus;
}
public DefiniteAssignmentStatus GetStatusAfter(Statement statement)
{
return nodeStatus[endNodeDict[statement]];
return endNodeDict[statement].NodeStatus;
}
public DefiniteAssignmentStatus GetStatusBeforeLoopCondition(Statement statement)
{
return nodeStatus[conditionNodeDict[statement]];
return conditionNodeDict[statement].NodeStatus;
}
/// <summary>
@ -161,7 +220,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -161,7 +220,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
GraphVizGraph g = new GraphVizGraph();
g.Title = "DefiniteAssignment - " + variableName;
for (int i = 0; i < allNodes.Count; i++) {
string name = nodeStatus[allNodes[i]].ToString() + Environment.NewLine;
string name = "#" + i + " = " + allNodes[i].NodeStatus.ToString() + Environment.NewLine;
switch (allNodes[i].Type) {
case ControlFlowNodeType.StartNode:
case ControlFlowNodeType.BetweenStatements:
@ -179,7 +238,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -179,7 +238,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
}
g.AddNode(new GraphVizNode(i) { label = name });
foreach (ControlFlowEdge edge in allNodes[i].Outgoing) {
GraphVizEdge ge = new GraphVizEdge(i, allNodes.IndexOf(edge.To));
GraphVizEdge ge = new GraphVizEdge(i, ((DefiniteAssignmentNode)edge.To).Index);
if (edgeStatus.Count > 0)
ge.label = edgeStatus[edge].ToString();
if (edge.IsLeavingTryFinally)
@ -218,11 +277,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -218,11 +277,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
return DefiniteAssignmentStatus.PotentiallyAssigned;
}
void ChangeNodeStatus(ControlFlowNode node, DefiniteAssignmentStatus inputStatus)
void ChangeNodeStatus(DefiniteAssignmentNode node, DefiniteAssignmentStatus inputStatus)
{
if (nodeStatus[node] == inputStatus)
if (node.NodeStatus == inputStatus)
return;
nodeStatus[node] = inputStatus;
node.NodeStatus = inputStatus;
DefiniteAssignmentStatus outputStatus;
switch (node.Type) {
case ControlFlowNodeType.StartNode:
@ -312,7 +371,9 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -312,7 +371,9 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
throw new InvalidOperationException("Invalid value for DefiniteAssignmentStatus");
}
edgeStatus[edge] = newStatus;
nodesWithModifiedInput.Enqueue(edge.To);
DefiniteAssignmentNode targetNode = (DefiniteAssignmentNode)edge.To;
if (analyzedRangeStart <= targetNode.Index && targetNode.Index <= analyzedRangeEnd)
nodesWithModifiedInput.Enqueue(targetNode);
}
/// <summary>
@ -321,7 +382,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -321,7 +382,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
ConstantResolveResult EvaluateConstant(Expression expr)
{
return cfgBuilder.EvaluateConstant(expr);
if (resolveVisitor.TypeResolveContext is MinimalResolveContext) {
if (!(expr is PrimitiveExpression || expr is NullReferenceExpression))
return null;
}
return resolveVisitor.Resolve(expr) as ConstantResolveResult;
}
/// <summary>
@ -330,7 +395,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -330,7 +395,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// <returns>The value of the constant boolean expression; or null if the value is not a constant boolean expression.</returns>
bool? EvaluateCondition(Expression expr)
{
return cfgBuilder.EvaluateCondition(expr);
ConstantResolveResult rr = EvaluateConstant(expr);
if (rr != null)
return rr.ConstantValue as bool?;
else
return null;
}
static DefiniteAssignmentStatus CleanSpecialValues(DefiniteAssignmentStatus status)
@ -633,10 +702,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -633,10 +702,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
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);
}
analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data);
return data;
}
@ -644,10 +710,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -644,10 +710,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
{
Statement body = lambdaExpression.Body as Statement;
if (body != null) {
foreach (ControlFlowNode node in analysis.allNodes) {
if (node.NextStatement == body)
analysis.ChangeNodeStatus(node, data);
}
analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data);
} else {
lambdaExpression.Body.AcceptVisitor(this, data);
}

Loading…
Cancel
Save