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 @@ -148,5 +148,54 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
Assert.AreEqual(DefiniteAssignmentStatus.CodeUnreachable, da.GetStatusAfter(loop.EmbeddedStatement));
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 @@ -146,7 +146,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken))
{
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)
@ -221,14 +222,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -221,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) {
@ -243,18 +245,28 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -243,18 +245,28 @@ 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
#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>
/// 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)
{
if (EvaluateOnlyPrimitiveConstants) {
if (!(expr is PrimitiveExpression || expr is NullReferenceExpression))
return null;
}
return resolveVisitor.Resolve(expr) as ConstantResolveResult;
}
@ -264,9 +276,9 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -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>
bool? EvaluateCondition(Expression expr)
{
ConstantResolveResult crr = EvaluateConstant(expr);
if (crr != null)
return crr.ConstantValue as bool?;
ConstantResolveResult rr = EvaluateConstant(expr);
if (rr != null)
return rr.ConstantValue as bool?;
else
return null;
}
@ -328,7 +340,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -328,7 +340,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
foreach (Statement stmt in statements) {
if (childNode == null) {
childNode = builder.CreateStartNode(stmt);
Connect(source, childNode);
if (source != null)
Connect(source, childNode);
}
Debug.Assert(childNode.NextStatement == stmt);
childNode = stmt.AcceptVisitor(this, childNode);
@ -407,7 +420,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -407,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) {
@ -428,6 +441,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -428,6 +441,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
throw new NotImplementedException();
}
builder.nodes.Add(end);
return end;
}
@ -446,7 +460,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -446,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);
@ -464,14 +478,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -464,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);
@ -488,6 +503,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -488,6 +503,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
breakTargets.Pop();
continueTargets.Pop();
builder.nodes.Add(conditionNode);
builder.nodes.Add(end);
return end;
}
@ -495,7 +512,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -495,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);
@ -525,6 +542,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -525,6 +542,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
if (cond != true)
Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse);
builder.nodes.Add(end);
return end;
}
@ -541,7 +559,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -541,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);
@ -555,7 +573,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -555,7 +573,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
continueTargets.Pop();
Connect(conditionNode, end);
builder.nodes.Add(end);
return end;
}
@ -591,7 +609,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -591,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);
@ -605,6 +623,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -605,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;
}

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

@ -44,22 +44,49 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -44,22 +44,49 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// </summary>
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 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 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<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))
: 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 @@ -72,25 +99,57 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
this.resolveVisitor = resolveVisitor;
this.cancellationToken = resolveVisitor.CancellationToken;
visitor.analysis = this;
ControlFlowGraphBuilder b = new ControlFlowGraphBuilder();
allNodes.AddRange(b.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(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));
if (resolveVisitor.TypeResolveContext is MinimalResolveContext) {
cfgBuilder.EvaluateOnlyPrimitiveConstants = true;
}
// 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)
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 @@ -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)
{
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]);
@ -128,12 +204,17 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -128,12 +204,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 conditionNodeDict[statement].NodeStatus;
}
/// <summary>
@ -144,7 +225,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -144,7 +225,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:
@ -162,7 +243,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -162,7 +243,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)
@ -201,11 +282,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -201,11 +282,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:
@ -277,25 +358,26 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -277,25 +358,26 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
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");
// Ensure that status can cannot change back to CodeUnreachable after it once was reachable.
// Also, don't ever use AssignedAfter... for statements.
if (newStatus == DefiniteAssignmentStatus.CodeUnreachable
|| newStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression
|| newStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
{
throw new InvalidOperationException();
}
// Note that the status can change from DefinitelyAssigned
// back to PotentiallyAssigned as unreachable input edges are
// discovered to be reachable.
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>
@ -304,6 +386,10 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -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>
ConstantResolveResult EvaluateConstant(Expression expr)
{
if (resolveVisitor.TypeResolveContext is MinimalResolveContext) {
if (!(expr is PrimitiveExpression || expr is NullReferenceExpression))
return null;
}
return resolveVisitor.Resolve(expr) as ConstantResolveResult;
}
@ -313,9 +399,9 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -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>
bool? EvaluateCondition(Expression expr)
{
ConstantResolveResult crr = EvaluateConstant(expr);
if (crr != null)
return crr.ConstantValue as bool?;
ConstantResolveResult rr = EvaluateConstant(expr);
if (rr != null)
return rr.ConstantValue as bool?;
else
return null;
}
@ -477,11 +563,14 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -477,11 +563,14 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
{
IdentifierExpression ident = left as IdentifierExpression;
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;
} else {
DefiniteAssignmentStatus status = left.AcceptVisitor(this, initialStatus);
status = right.AcceptVisitor(this, CleanSpecialValues(status));
if (right != null)
status = right.AcceptVisitor(this, CleanSpecialValues(status));
return CleanSpecialValues(status);
}
}
@ -620,10 +709,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -620,10 +709,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;
}
@ -631,10 +717,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -631,10 +717,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);
}

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

@ -0,0 +1,116 @@ @@ -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 @@ -193,5 +193,15 @@ namespace ICSharpCode.NRefactory.CSharp
}
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 @@ -38,6 +38,38 @@ namespace ICSharpCode.NRefactory.CSharp
}
#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()
{
return (Statement)base.Clone();

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

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

1
ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

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

2
ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs

@ -35,7 +35,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -35,7 +35,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
public bool IncludeInternalMembers { get; set; }
/// <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>
public IDocumentationProvider DocumentationProvider { get; set; }

4
ICSharpCode.NRefactory/TypeSystem/ITypeResolveContext.cs

@ -44,7 +44,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -44,7 +44,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// <param name="nameComparer">Language-specific rules for how namespace names are compared</param>
/// <returns>List of classes within that namespace.</returns>
/// <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.
/// </remarks>
IEnumerable<ITypeDefinition> GetClasses(string nameSpace, StringComparer nameComparer);
@ -53,7 +53,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -53,7 +53,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// Retrieves all namespaces.
/// </summary>
/// <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.
/// </remarks>
IEnumerable<string> GetNamespaces();

Loading…
Cancel
Save