Browse Source

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

newNRvisualizers
Eusebiu Marcu 15 years ago
parent
commit
01a3871281
  1. 49
      ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs
  2. 53
      ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs
  3. 205
      ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs
  4. 116
      ICSharpCode.NRefactory/CSharp/Analysis/MinimalResolveContext.cs
  5. 10
      ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs
  6. 32
      ICSharpCode.NRefactory/CSharp/Ast/Statements/Statement.cs
  7. 2
      ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs
  8. 1
      ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj
  9. 2
      ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
  10. 4
      ICSharpCode.NRefactory/TypeSystem/ITypeResolveContext.cs

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

@ -148,5 +148,54 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
Assert.AreEqual(DefiniteAssignmentStatus.CodeUnreachable, da.GetStatusAfter(loop.EmbeddedStatement)); Assert.AreEqual(DefiniteAssignmentStatus.CodeUnreachable, da.GetStatusAfter(loop.EmbeddedStatement));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop)); Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop));
} }
[Test]
public void ForLoop()
{
ForStatement loop = new ForStatement {
Initializers = {
new ExpressionStatement(
new AssignmentExpression(new IdentifierExpression("i"), new PrimitiveExpression(0))
)
},
Condition = new BinaryOperatorExpression(new IdentifierExpression("i"), BinaryOperatorType.LessThan, new PrimitiveExpression(1000)),
Iterators = {
new ExpressionStatement(
new AssignmentExpression {
Left = new IdentifierExpression("i"),
Operator = AssignmentOperatorType.Add,
Right = new IdentifierExpression("j")
}
)
},
EmbeddedStatement = new ExpressionStatement(
new AssignmentExpression(new IdentifierExpression("j"), new IdentifierExpression("i"))
)};
DefiniteAssignmentAnalysis da = new DefiniteAssignmentAnalysis(loop, CecilLoaderTests.Mscorlib);
da.Analyze("i");
Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(loop));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(loop.Initializers.Single()));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop.Initializers.Single()));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBeforeLoopCondition(loop));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(loop.EmbeddedStatement));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop.EmbeddedStatement));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(loop.Iterators.Single()));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop.Iterators.Single()));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop));
da.Analyze("j");
Assert.AreEqual(0, da.UnassignedVariableUses.Count);
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(loop));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(loop.Initializers.Single()));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusAfter(loop.Initializers.Single()));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBeforeLoopCondition(loop));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(loop.EmbeddedStatement));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop.EmbeddedStatement));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(loop.Iterators.Single()));
Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop.Iterators.Single()));
Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusAfter(loop));
}
} }
} }

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

@ -146,7 +146,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken)) public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken))
{ {
return BuildControlFlowGraph(statement, new ResolveVisitor( return BuildControlFlowGraph(statement, new ResolveVisitor(
new CSharpResolver(context, cancellationToken), null, ConstantModeResolveVisitorNavigator.Skip)); new CSharpResolver(context, cancellationToken),
null, ConstantModeResolveVisitorNavigator.Skip));
} }
public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, ResolveVisitor resolveVisitor) public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, ResolveVisitor resolveVisitor)
@ -221,14 +222,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
return node; return node;
} }
ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type) ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type, bool addToNodeList = true)
{ {
ControlFlowNode node = CreateNode(null, statement, type); ControlFlowNode node = CreateNode(null, statement, type);
nodes.Add(node); if (addToNodeList)
nodes.Add(node);
return node; return node;
} }
ControlFlowNode CreateEndNode(Statement statement) ControlFlowNode CreateEndNode(Statement statement, bool addToNodeList = true)
{ {
Statement nextStatement; Statement nextStatement;
if (statement == rootStatement) { if (statement == rootStatement) {
@ -243,18 +245,28 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
} }
ControlFlowNodeType type = nextStatement != null ? ControlFlowNodeType.BetweenStatements : ControlFlowNodeType.EndNode; ControlFlowNodeType type = nextStatement != null ? ControlFlowNodeType.BetweenStatements : ControlFlowNodeType.EndNode;
ControlFlowNode node = CreateNode(statement, nextStatement, type); ControlFlowNode node = CreateNode(statement, nextStatement, type);
nodes.Add(node); if (addToNodeList)
nodes.Add(node);
return node; return node;
} }
#endregion #endregion
#region Constant evaluation #region Constant evaluation
/// <summary>
/// Gets/Sets whether to handle only primitive expressions as constants (no complex expressions like "a + b").
/// </summary>
public bool EvaluateOnlyPrimitiveConstants { get; set; }
/// <summary> /// <summary>
/// Evaluates an expression. /// Evaluates an expression.
/// </summary> /// </summary>
/// <returns>The constant value of the expression; or null if the expression is not a constant.</returns> /// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
ConstantResolveResult EvaluateConstant(Expression expr) ConstantResolveResult EvaluateConstant(Expression expr)
{ {
if (EvaluateOnlyPrimitiveConstants) {
if (!(expr is PrimitiveExpression || expr is NullReferenceExpression))
return null;
}
return resolveVisitor.Resolve(expr) as ConstantResolveResult; return resolveVisitor.Resolve(expr) as ConstantResolveResult;
} }
@ -264,9 +276,9 @@ 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> /// <returns>The value of the constant boolean expression; or null if the value is not a constant boolean expression.</returns>
bool? EvaluateCondition(Expression expr) bool? EvaluateCondition(Expression expr)
{ {
ConstantResolveResult crr = EvaluateConstant(expr); ConstantResolveResult rr = EvaluateConstant(expr);
if (crr != null) if (rr != null)
return crr.ConstantValue as bool?; return rr.ConstantValue as bool?;
else else
return null; return null;
} }
@ -328,7 +340,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
foreach (Statement stmt in statements) { foreach (Statement stmt in statements) {
if (childNode == null) { if (childNode == null) {
childNode = builder.CreateStartNode(stmt); childNode = builder.CreateStartNode(stmt);
Connect(source, childNode); if (source != null)
Connect(source, childNode);
} }
Debug.Assert(childNode.NextStatement == stmt); Debug.Assert(childNode.NextStatement == stmt);
childNode = stmt.AcceptVisitor(this, childNode); childNode = stmt.AcceptVisitor(this, childNode);
@ -407,7 +420,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
int gotoCaseOrDefaultInOuterScope = gotoCaseOrDefault.Count; int gotoCaseOrDefaultInOuterScope = gotoCaseOrDefault.Count;
ControlFlowNode end = builder.CreateEndNode(switchStatement); ControlFlowNode end = builder.CreateEndNode(switchStatement, addToNodeList: false);
breakTargets.Push(end); breakTargets.Push(end);
foreach (SwitchSection section in switchStatement.SwitchSections) { foreach (SwitchSection section in switchStatement.SwitchSections) {
if (constant == null || section == sectionMatchedByConstant) { if (constant == null || section == sectionMatchedByConstant) {
@ -428,6 +441,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
throw new NotImplementedException(); throw new NotImplementedException();
} }
builder.nodes.Add(end);
return end; return end;
} }
@ -446,7 +460,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override ControlFlowNode VisitWhileStatement(WhileStatement whileStatement, ControlFlowNode data) public override ControlFlowNode VisitWhileStatement(WhileStatement whileStatement, ControlFlowNode data)
{ {
// <data> <condition> while (cond) { <bodyStart> embeddedStmt; <bodyEnd> } <end> // <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); ControlFlowNode conditionNode = builder.CreateSpecialNode(whileStatement, ControlFlowNodeType.LoopCondition);
breakTargets.Push(end); breakTargets.Push(end);
continueTargets.Push(conditionNode); continueTargets.Push(conditionNode);
@ -464,14 +478,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
breakTargets.Pop(); breakTargets.Pop();
continueTargets.Pop(); continueTargets.Pop();
builder.nodes.Add(end);
return end; return end;
} }
public override ControlFlowNode VisitDoWhileStatement(DoWhileStatement doWhileStatement, ControlFlowNode data) public override ControlFlowNode VisitDoWhileStatement(DoWhileStatement doWhileStatement, ControlFlowNode data)
{ {
// <data> do { <bodyStart> embeddedStmt; <bodyEnd>} <condition> while(cond); <end> // <data> do { <bodyStart> embeddedStmt; <bodyEnd>} <condition> while(cond); <end>
ControlFlowNode end = builder.CreateEndNode(doWhileStatement); ControlFlowNode end = builder.CreateEndNode(doWhileStatement, addToNodeList: false);
ControlFlowNode conditionNode = builder.CreateSpecialNode(doWhileStatement, ControlFlowNodeType.LoopCondition); ControlFlowNode conditionNode = builder.CreateSpecialNode(doWhileStatement, ControlFlowNodeType.LoopCondition, addToNodeList: false);
breakTargets.Push(end); breakTargets.Push(end);
continueTargets.Push(conditionNode); continueTargets.Push(conditionNode);
@ -488,6 +503,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
breakTargets.Pop(); breakTargets.Pop();
continueTargets.Pop(); continueTargets.Pop();
builder.nodes.Add(conditionNode);
builder.nodes.Add(end);
return end; return end;
} }
@ -495,7 +512,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
{ {
data = HandleStatementList(forStatement.Initializers, data); data = HandleStatementList(forStatement.Initializers, data);
// for (initializers <data>; <condition>cond; <iteratorStart>iterators<iteratorEnd>) { <bodyStart> embeddedStmt; <bodyEnd> } <end> // 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); ControlFlowNode conditionNode = builder.CreateSpecialNode(forStatement, ControlFlowNodeType.LoopCondition);
Connect(data, conditionNode); Connect(data, conditionNode);
@ -525,6 +542,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
if (cond != true) if (cond != true)
Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse); Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse);
builder.nodes.Add(end);
return end; return end;
} }
@ -541,7 +559,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override ControlFlowNode VisitForeachStatement(ForeachStatement foreachStatement, ControlFlowNode data) public override ControlFlowNode VisitForeachStatement(ForeachStatement foreachStatement, ControlFlowNode data)
{ {
// <data> foreach (<condition>...) { <bodyStart>embeddedStmt<bodyEnd> } <end> // <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); ControlFlowNode conditionNode = builder.CreateSpecialNode(foreachStatement, ControlFlowNodeType.LoopCondition);
Connect(data, conditionNode); Connect(data, conditionNode);
@ -555,7 +573,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
continueTargets.Pop(); continueTargets.Pop();
Connect(conditionNode, end); Connect(conditionNode, end);
builder.nodes.Add(end);
return end; return end;
} }
@ -591,7 +609,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override ControlFlowNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement, ControlFlowNode data) 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); var edge = Connect(HandleEmbeddedStatement(tryCatchStatement.TryBlock, data), end);
if (!tryCatchStatement.FinallyBlock.IsNull) if (!tryCatchStatement.FinallyBlock.IsNull)
edge.AddJumpOutOfTryFinally(tryCatchStatement); edge.AddJumpOutOfTryFinally(tryCatchStatement);
@ -605,6 +623,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
// Consumers of the CFG will have to special-case try-finally. // Consumers of the CFG will have to special-case try-finally.
HandleEmbeddedStatement(tryCatchStatement.FinallyBlock, data); HandleEmbeddedStatement(tryCatchStatement.FinallyBlock, data);
} }
builder.nodes.Add(end);
return end; return end;
} }

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

@ -44,22 +44,49 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// </summary> /// </summary>
public class DefiniteAssignmentAnalysis public class DefiniteAssignmentAnalysis
{ {
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 DefiniteAssignmentVisitor visitor = new DefiniteAssignmentVisitor();
readonly List<ControlFlowNode> allNodes = new List<ControlFlowNode>(); readonly List<DefiniteAssignmentNode> allNodes = new List<DefiniteAssignmentNode>();
readonly Dictionary<Statement, ControlFlowNode> beginNodeDict = new Dictionary<Statement, ControlFlowNode>(); readonly Dictionary<Statement, DefiniteAssignmentNode> beginNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>();
readonly Dictionary<Statement, ControlFlowNode> endNodeDict = new Dictionary<Statement, ControlFlowNode>(); readonly Dictionary<Statement, DefiniteAssignmentNode> endNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>();
readonly Dictionary<Statement, DefiniteAssignmentNode> conditionNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>();
readonly ResolveVisitor resolveVisitor; readonly ResolveVisitor resolveVisitor;
readonly CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
Dictionary<ControlFlowNode, DefiniteAssignmentStatus> nodeStatus = new Dictionary<ControlFlowNode, DefiniteAssignmentStatus>();
Dictionary<ControlFlowEdge, DefiniteAssignmentStatus> edgeStatus = new Dictionary<ControlFlowEdge, DefiniteAssignmentStatus>(); Dictionary<ControlFlowEdge, DefiniteAssignmentStatus> edgeStatus = new Dictionary<ControlFlowEdge, DefiniteAssignmentStatus>();
string variableName; string variableName;
List<IdentifierExpression> unassignedVariableUses = new List<IdentifierExpression>(); List<IdentifierExpression> unassignedVariableUses = new List<IdentifierExpression>();
int analyzedRangeStart, analyzedRangeEnd;
Queue<DefiniteAssignmentNode> nodesWithModifiedInput = new Queue<DefiniteAssignmentNode>();
Queue<ControlFlowNode> nodesWithModifiedInput = new Queue<ControlFlowNode>(); public DefiniteAssignmentAnalysis(Statement rootStatement, CancellationToken cancellationToken = default(CancellationToken))
: this(rootStatement, null, cancellationToken)
{
}
public DefiniteAssignmentAnalysis(Statement rootStatement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken)) public DefiniteAssignmentAnalysis(Statement rootStatement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken))
: this(rootStatement, new ResolveVisitor(new CSharpResolver(context, cancellationToken), null, ConstantModeResolveVisitorNavigator.Skip)) : this(rootStatement, new ResolveVisitor(new CSharpResolver(context ?? MinimalResolveContext.Instance, cancellationToken),
null, ConstantModeResolveVisitorNavigator.Skip))
{ {
} }
@ -72,25 +99,57 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
this.resolveVisitor = resolveVisitor; this.resolveVisitor = resolveVisitor;
this.cancellationToken = resolveVisitor.CancellationToken; this.cancellationToken = resolveVisitor.CancellationToken;
visitor.analysis = this; visitor.analysis = this;
ControlFlowGraphBuilder b = new ControlFlowGraphBuilder(); if (resolveVisitor.TypeResolveContext is MinimalResolveContext) {
allNodes.AddRange(b.BuildControlFlowGraph(rootStatement, resolveVisitor)); cfgBuilder.EvaluateOnlyPrimitiveConstants = true;
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, resolveVisitor));
LambdaExpression lambda = descendant as LambdaExpression;
if (lambda != null && lambda.Body is Statement)
allNodes.AddRange(b.BuildControlFlowGraph((Statement)lambda.Body, resolveVisitor));
} }
// Verify that we created nodes for all statements: allNodes.AddRange(cfgBuilder.BuildControlFlowGraph(rootStatement, resolveVisitor).Cast<DefiniteAssignmentNode>());
Debug.Assert(!rootStatement.DescendantsAndSelf.OfType<Statement>().Except(allNodes.Select(n => n.NextStatement)).Any()); for (int i = 0; i < allNodes.Count; i++) {
// Now register the nodes in the dictionaries: DefiniteAssignmentNode node = allNodes[i];
foreach (ControlFlowNode node in allNodes) { 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) if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements)
beginNodeDict.Add(node.NextStatement, node); beginNodeDict.Add(node.NextStatement, node);
if (node.Type == ControlFlowNodeType.BetweenStatements || node.Type == ControlFlowNodeType.EndNode) if (node.Type == ControlFlowNodeType.BetweenStatements || node.Type == ControlFlowNodeType.EndNode)
endNodeDict.Add(node.PreviousStatement, node); endNodeDict.Add(node.PreviousStatement, node);
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());
// Verify that we put all nodes into the dictionaries:
Debug.Assert(rootStatement.DescendantsAndSelf.OfType<Statement>().All(stmt => beginNodeDict.ContainsKey(stmt)));
Debug.Assert(rootStatement.DescendantsAndSelf.OfType<Statement>().All(stmt => endNodeDict.ContainsKey(stmt)));
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);
} }
} }
@ -103,21 +162,38 @@ 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)
{
Debug.Assert(beginNodeDict.ContainsKey(start) && endNodeDict.ContainsKey(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) public void Analyze(string variable, DefiniteAssignmentStatus initialStatus = DefiniteAssignmentStatus.PotentiallyAssigned)
{ {
this.variableName = variable; this.variableName = variable;
// Reset the status: // Reset the status:
unassignedVariableUses.Clear(); unassignedVariableUses.Clear();
foreach (ControlFlowNode node in allNodes) { foreach (DefiniteAssignmentNode node in allNodes) {
nodeStatus[node] = DefiniteAssignmentStatus.CodeUnreachable; node.NodeStatus = DefiniteAssignmentStatus.CodeUnreachable;
foreach (ControlFlowEdge edge in node.Outgoing) foreach (ControlFlowEdge edge in node.Outgoing)
edgeStatus[edge] = DefiniteAssignmentStatus.CodeUnreachable; edgeStatus[edge] = DefiniteAssignmentStatus.CodeUnreachable;
} }
ChangeNodeStatus(allNodes[0], initialStatus); ChangeNodeStatus(allNodes[analyzedRangeStart], initialStatus);
// Iterate as long as the input status of some nodes is changing: // Iterate as long as the input status of some nodes is changing:
while (nodesWithModifiedInput.Count > 0) { while (nodesWithModifiedInput.Count > 0) {
ControlFlowNode node = nodesWithModifiedInput.Dequeue(); DefiniteAssignmentNode node = nodesWithModifiedInput.Dequeue();
DefiniteAssignmentStatus inputStatus = DefiniteAssignmentStatus.CodeUnreachable; DefiniteAssignmentStatus inputStatus = DefiniteAssignmentStatus.CodeUnreachable;
foreach (ControlFlowEdge edge in node.Incoming) { foreach (ControlFlowEdge edge in node.Incoming) {
inputStatus = MergeStatus(inputStatus, edgeStatus[edge]); inputStatus = MergeStatus(inputStatus, edgeStatus[edge]);
@ -128,12 +204,17 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public DefiniteAssignmentStatus GetStatusBefore(Statement statement) public DefiniteAssignmentStatus GetStatusBefore(Statement statement)
{ {
return nodeStatus[beginNodeDict[statement]]; return beginNodeDict[statement].NodeStatus;
} }
public DefiniteAssignmentStatus GetStatusAfter(Statement statement) public DefiniteAssignmentStatus GetStatusAfter(Statement statement)
{ {
return nodeStatus[endNodeDict[statement]]; return endNodeDict[statement].NodeStatus;
}
public DefiniteAssignmentStatus GetStatusBeforeLoopCondition(Statement statement)
{
return conditionNodeDict[statement].NodeStatus;
} }
/// <summary> /// <summary>
@ -144,7 +225,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
GraphVizGraph g = new GraphVizGraph(); GraphVizGraph g = new GraphVizGraph();
g.Title = "DefiniteAssignment - " + variableName; g.Title = "DefiniteAssignment - " + variableName;
for (int i = 0; i < allNodes.Count; i++) { 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) { switch (allNodes[i].Type) {
case ControlFlowNodeType.StartNode: case ControlFlowNodeType.StartNode:
case ControlFlowNodeType.BetweenStatements: case ControlFlowNodeType.BetweenStatements:
@ -162,7 +243,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
} }
g.AddNode(new GraphVizNode(i) { label = name }); g.AddNode(new GraphVizNode(i) { label = name });
foreach (ControlFlowEdge edge in allNodes[i].Outgoing) { 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) if (edgeStatus.Count > 0)
ge.label = edgeStatus[edge].ToString(); ge.label = edgeStatus[edge].ToString();
if (edge.IsLeavingTryFinally) if (edge.IsLeavingTryFinally)
@ -201,11 +282,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
return DefiniteAssignmentStatus.PotentiallyAssigned; return DefiniteAssignmentStatus.PotentiallyAssigned;
} }
void ChangeNodeStatus(ControlFlowNode node, DefiniteAssignmentStatus inputStatus) void ChangeNodeStatus(DefiniteAssignmentNode node, DefiniteAssignmentStatus inputStatus)
{ {
if (nodeStatus[node] == inputStatus) if (node.NodeStatus == inputStatus)
return; return;
nodeStatus[node] = inputStatus; node.NodeStatus = inputStatus;
DefiniteAssignmentStatus outputStatus; DefiniteAssignmentStatus outputStatus;
switch (node.Type) { switch (node.Type) {
case ControlFlowNodeType.StartNode: case ControlFlowNodeType.StartNode:
@ -277,25 +358,26 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
DefiniteAssignmentStatus oldStatus = edgeStatus[edge]; DefiniteAssignmentStatus oldStatus = edgeStatus[edge];
if (oldStatus == newStatus) if (oldStatus == newStatus)
return; return;
// Ensure that status can change only in one direction: // Ensure that status can cannot change back to CodeUnreachable after it once was reachable.
// CodeUnreachable -> PotentiallyAssigned -> DefinitelyAssigned // Also, don't ever use AssignedAfter... for statements.
// Going against this direction indicates a bug and could cause infinite loops. if (newStatus == DefiniteAssignmentStatus.CodeUnreachable
switch (oldStatus) { || newStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression
case DefiniteAssignmentStatus.PotentiallyAssigned: || newStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
if (newStatus != DefiniteAssignmentStatus.DefinitelyAssigned) {
throw new InvalidOperationException("Invalid state transition"); throw new InvalidOperationException();
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");
} }
// Note that the status can change from DefinitelyAssigned
// back to PotentiallyAssigned as unreachable input edges are
// discovered to be reachable.
edgeStatus[edge] = newStatus; edgeStatus[edge] = newStatus;
nodesWithModifiedInput.Enqueue(edge.To); DefiniteAssignmentNode targetNode = (DefiniteAssignmentNode)edge.To;
if (analyzedRangeStart <= targetNode.Index && targetNode.Index <= analyzedRangeEnd) {
// TODO: potential optimization: visit previously unreachable nodes with higher priority
// (e.g. use Deque and enqueue previously unreachable nodes at the front, but
// other nodes at the end)
nodesWithModifiedInput.Enqueue(targetNode);
}
} }
/// <summary> /// <summary>
@ -304,6 +386,10 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// <returns>The constant value of the expression; or null if the expression is not a constant.</returns> /// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
ConstantResolveResult EvaluateConstant(Expression expr) ConstantResolveResult EvaluateConstant(Expression expr)
{ {
if (resolveVisitor.TypeResolveContext is MinimalResolveContext) {
if (!(expr is PrimitiveExpression || expr is NullReferenceExpression))
return null;
}
return resolveVisitor.Resolve(expr) as ConstantResolveResult; return resolveVisitor.Resolve(expr) as ConstantResolveResult;
} }
@ -313,9 +399,9 @@ 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> /// <returns>The value of the constant boolean expression; or null if the value is not a constant boolean expression.</returns>
bool? EvaluateCondition(Expression expr) bool? EvaluateCondition(Expression expr)
{ {
ConstantResolveResult crr = EvaluateConstant(expr); ConstantResolveResult rr = EvaluateConstant(expr);
if (crr != null) if (rr != null)
return crr.ConstantValue as bool?; return rr.ConstantValue as bool?;
else else
return null; return null;
} }
@ -477,11 +563,14 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
{ {
IdentifierExpression ident = left as IdentifierExpression; IdentifierExpression ident = left as IdentifierExpression;
if (ident != null && ident.Identifier == analysis.variableName) { if (ident != null && ident.Identifier == analysis.variableName) {
right.AcceptVisitor(this, initialStatus); // right==null is special case when handling 'out' expressions
if (right != null)
right.AcceptVisitor(this, initialStatus);
return DefiniteAssignmentStatus.DefinitelyAssigned; return DefiniteAssignmentStatus.DefinitelyAssigned;
} else { } else {
DefiniteAssignmentStatus status = left.AcceptVisitor(this, initialStatus); DefiniteAssignmentStatus status = left.AcceptVisitor(this, initialStatus);
status = right.AcceptVisitor(this, CleanSpecialValues(status)); if (right != null)
status = right.AcceptVisitor(this, CleanSpecialValues(status));
return CleanSpecialValues(status); return CleanSpecialValues(status);
} }
} }
@ -620,10 +709,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override DefiniteAssignmentStatus VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, DefiniteAssignmentStatus data) public override DefiniteAssignmentStatus VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, DefiniteAssignmentStatus data)
{ {
BlockStatement body = anonymousMethodExpression.Body; BlockStatement body = anonymousMethodExpression.Body;
foreach (ControlFlowNode node in analysis.allNodes) { analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data);
if (node.NextStatement == body)
analysis.ChangeNodeStatus(node, data);
}
return data; return data;
} }
@ -631,10 +717,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
{ {
Statement body = lambdaExpression.Body as Statement; Statement body = lambdaExpression.Body as Statement;
if (body != null) { if (body != null) {
foreach (ControlFlowNode node in analysis.allNodes) { analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data);
if (node.NextStatement == body)
analysis.ChangeNodeStatus(node, data);
}
} else { } else {
lambdaExpression.Body.AcceptVisitor(this, data); lambdaExpression.Body.AcceptVisitor(this, data);
} }

116
ICSharpCode.NRefactory/CSharp/Analysis/MinimalResolveContext.cs

@ -0,0 +1,116 @@
// 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.Collections.ObjectModel;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
namespace ICSharpCode.NRefactory.CSharp.Analysis
{
/// <summary>
/// Resolve context represents the minimal mscorlib required for evaluating constants.
/// </summary>
sealed class MinimalResolveContext : IProjectContent, ISynchronizedTypeResolveContext
{
static readonly Lazy<MinimalResolveContext> instance = new Lazy<MinimalResolveContext>(() => new MinimalResolveContext());
public static MinimalResolveContext Instance {
get { return instance.Value; }
}
readonly ReadOnlyCollection<string> namespaces = Array.AsReadOnly(new string[] { "System" });
readonly IAttribute[] assemblyAttributes = new IAttribute[0];
readonly ITypeDefinition systemObject, systemValueType;
readonly ReadOnlyCollection<ITypeDefinition> types;
private MinimalResolveContext()
{
List<ITypeDefinition> types = new List<ITypeDefinition>();
types.Add(systemObject = new DefaultTypeDefinition(this, "System", "Object"));
types.Add(systemValueType = new DefaultTypeDefinition(this, "System", "ValueType") { BaseTypes = { systemObject } });
types.Add(CreateStruct("System", "Boolean"));
types.Add(CreateStruct("System", "SByte"));
types.Add(CreateStruct("System", "Byte"));
types.Add(CreateStruct("System", "Int16"));
types.Add(CreateStruct("System", "UInt16"));
types.Add(CreateStruct("System", "Int32"));
types.Add(CreateStruct("System", "UInt32"));
types.Add(CreateStruct("System", "Int64"));
types.Add(CreateStruct("System", "UInt64"));
types.Add(CreateStruct("System", "Single"));
types.Add(CreateStruct("System", "Double"));
types.Add(CreateStruct("System", "Decimal"));
types.Add(new DefaultTypeDefinition(this, "System", "String") { BaseTypes = { systemObject } });
foreach (ITypeDefinition type in types)
type.Freeze();
this.types = types.AsReadOnly();
}
ITypeDefinition CreateStruct(string nameSpace, string name)
{
return new DefaultTypeDefinition(this, nameSpace, name) {
ClassType = ClassType.Struct,
BaseTypes = { systemValueType }
};
}
public ITypeDefinition GetClass(string nameSpace, string name, int typeParameterCount, StringComparer nameComparer)
{
foreach (ITypeDefinition type in types) {
if (nameComparer.Equals(type.Name, name) && nameComparer.Equals(type.Namespace, nameSpace) && type.TypeParameterCount == typeParameterCount)
return type;
}
return null;
}
public IEnumerable<ITypeDefinition> GetClasses()
{
return types;
}
public IEnumerable<ITypeDefinition> GetClasses(string nameSpace, StringComparer nameComparer)
{
return types.Where(t => nameComparer.Equals(t.Namespace, nameSpace));
}
public IEnumerable<string> GetNamespaces()
{
return namespaces;
}
public string GetNamespace(string nameSpace, StringComparer nameComparer)
{
foreach (string ns in namespaces) {
if (nameComparer.Equals(ns, nameSpace))
return ns;
}
return null;
}
public IList<IAttribute> AssemblyAttributes {
get { return assemblyAttributes; }
}
ICSharpCode.NRefactory.Utils.CacheManager ITypeResolveContext.CacheManager {
get {
// We don't support caching
return null;
}
}
ISynchronizedTypeResolveContext ITypeResolveContext.Synchronize()
{
// This class is immutable
return this;
}
void IDisposable.Dispose()
{
// exit from Synchronize() block
}
}
}

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

@ -193,5 +193,15 @@ namespace ICSharpCode.NRefactory.CSharp
} }
return false; return false;
} }
public void InsertAfter(T existingItem, T newItem)
{
node.InsertChildAfter(existingItem, newItem, role);
}
public void InsertBefore(T existingItem, T newItem)
{
node.InsertChildBefore(existingItem, newItem, role);
}
} }
} }

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

@ -38,6 +38,38 @@ namespace ICSharpCode.NRefactory.CSharp
} }
#endregion #endregion
/// <summary>
/// Gets the previous statement within the current block.
/// This is usually equivalent to <see cref="PrevSibling"/>, but will skip any non-statements (e.g. comments)
/// </summary>
public Statement PreviousStatement {
get {
AstNode node = this;
while ((node = node.PrevSibling) != null) {
Statement stmt = node as Statement;
if (stmt != null)
return stmt;
}
return null;
}
}
/// <summary>
/// Gets the next statement within the current block.
/// This is usually equivalent to <see cref="NextSibling"/>, but will skip any non-statements (e.g. comments)
/// </summary>
public Statement NextStatement {
get {
AstNode node = this;
while ((node = node.NextSibling) != null) {
Statement stmt = node as Statement;
if (stmt != null)
return stmt;
}
return null;
}
}
public new Statement Clone() public new Statement Clone()
{ {
return (Statement)base.Clone(); return (Statement)base.Clone();

2
ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs

@ -428,7 +428,7 @@ namespace ICSharpCode.NRefactory.CSharp
if (block != null) if (block != null)
VisitBlockStatement(block, null); VisitBlockStatement(block, null);
else else
throw new NotImplementedException(); embeddedStatement.AcceptVisitor(this, null);
} }
void WriteMethodBody(BlockStatement body) void WriteMethodBody(BlockStatement body)

1
ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -63,6 +63,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="CSharp\Analysis\ControlFlow.cs" /> <Compile Include="CSharp\Analysis\ControlFlow.cs" />
<Compile Include="CSharp\Analysis\DefiniteAssignmentAnalysis.cs" /> <Compile Include="CSharp\Analysis\DefiniteAssignmentAnalysis.cs" />
<Compile Include="CSharp\Analysis\MinimalResolveContext.cs" />
<Compile Include="CSharp\Ast\AstNodeCollection.cs" /> <Compile Include="CSharp\Ast\AstNodeCollection.cs" />
<Compile Include="CSharp\Ast\Expressions\TypeReferenceExpression.cs" /> <Compile Include="CSharp\Ast\Expressions\TypeReferenceExpression.cs" />
<Compile Include="CSharp\Ast\IAstVisitor.cs" /> <Compile Include="CSharp\Ast\IAstVisitor.cs" />

2
ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs

@ -35,7 +35,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
public bool IncludeInternalMembers { get; set; } public bool IncludeInternalMembers { get; set; }
/// <summary> /// <summary>
/// Gets/Sets the documentation provider that is used to retrive the XML documentation for all members. /// Gets/Sets the documentation provider that is used to retrieve the XML documentation for all members.
/// </summary> /// </summary>
public IDocumentationProvider DocumentationProvider { get; set; } public IDocumentationProvider DocumentationProvider { get; set; }

4
ICSharpCode.NRefactory/TypeSystem/ITypeResolveContext.cs

@ -44,7 +44,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// <param name="nameComparer">Language-specific rules for how namespace names are compared</param> /// <param name="nameComparer">Language-specific rules for how namespace names are compared</param>
/// <returns>List of classes within that namespace.</returns> /// <returns>List of classes within that namespace.</returns>
/// <remarks> /// <remarks>
/// If this method is called within <c>using (pc.Synchronize())</c>, then the returned enumerable is valid /// If this method is called within <c>using (var spc = pc.Synchronize())</c>, then the returned enumerable is valid
/// only until the end of the synchronize block. /// only until the end of the synchronize block.
/// </remarks> /// </remarks>
IEnumerable<ITypeDefinition> GetClasses(string nameSpace, StringComparer nameComparer); IEnumerable<ITypeDefinition> GetClasses(string nameSpace, StringComparer nameComparer);
@ -53,7 +53,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// Retrieves all namespaces. /// Retrieves all namespaces.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// If this method is called within <c>using (pc.Synchronize())</c>, then the returned enumerable is valid /// If this method is called within <c>using (var spc = pc.Synchronize())</c>, then the returned enumerable is valid
/// only until the end of the synchronize block. /// only until the end of the synchronize block.
/// </remarks> /// </remarks>
IEnumerable<string> GetNamespaces(); IEnumerable<string> GetNamespaces();

Loading…
Cancel
Save