mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
540 lines
17 KiB
540 lines
17 KiB
// |
|
// VariableReferenceGraph.cs |
|
// |
|
// Author: |
|
// Mansheng Yang <lightyang0@gmail.com> |
|
// |
|
// Copyright (c) 2012 Mansheng Yang <lightyang0@gmail.com> |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
|
// of this software and associated documentation files (the "Software"), to deal |
|
// in the Software without restriction, including without limitation the rights |
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
// copies of the Software, and to permit persons to whom the Software is |
|
// furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in |
|
// all copies or substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
// THE SOFTWARE. |
|
|
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using ICSharpCode.NRefactory.CSharp.Analysis; |
|
using ICSharpCode.NRefactory.CSharp.Resolver; |
|
|
|
namespace ICSharpCode.NRefactory.CSharp.Refactoring |
|
{ |
|
class VariableReferenceNode |
|
{ |
|
public IList<AstNode> References { |
|
get; |
|
private set; |
|
} |
|
|
|
public IList<VariableReferenceNode> NextNodes { |
|
get; |
|
private set; |
|
} |
|
|
|
public IList<VariableReferenceNode> PreviousNodes { |
|
get; |
|
private set; |
|
} |
|
|
|
public VariableReferenceNode () |
|
{ |
|
References = new List<AstNode> (); |
|
NextNodes = new List<VariableReferenceNode> (); |
|
PreviousNodes = new List<VariableReferenceNode> (); |
|
} |
|
|
|
public void AddNextNode (VariableReferenceNode node) |
|
{ |
|
NextNodes.Add (node); |
|
node.PreviousNodes.Add (this); |
|
} |
|
} |
|
|
|
class VariableReferenceGraphBuilder |
|
{ |
|
static ControlFlowGraphBuilder cfgBuilder = new ControlFlowGraphBuilder (); |
|
static CfgVariableReferenceNodeBuilder cfgVrNodeBuilder = new CfgVariableReferenceNodeBuilder (); |
|
|
|
public static VariableReferenceNode Build (ISet<AstNode> references, CSharpAstResolver resolver, |
|
Expression expression) |
|
{ |
|
return ExpressionNodeCreationVisitor.CreateNode (references, resolver, new [] { expression }); |
|
} |
|
|
|
public static VariableReferenceNode Build (Statement statement, ISet<AstNode> references, |
|
ISet<Statement> refStatements, BaseRefactoringContext context) |
|
{ |
|
var cfg = cfgBuilder.BuildControlFlowGraph (statement, context.Resolver, context.CancellationToken); |
|
return cfgVrNodeBuilder.Build (cfg [0], references, refStatements, context.Resolver); |
|
} |
|
|
|
class GetExpressionsVisitor : DepthFirstAstVisitor<IEnumerable<Expression>> |
|
{ |
|
|
|
public override IEnumerable<Expression> VisitIfElseStatement (IfElseStatement ifElseStatement) |
|
{ |
|
yield return ifElseStatement.Condition; |
|
} |
|
|
|
public override IEnumerable<Expression> VisitSwitchStatement (SwitchStatement switchStatement) |
|
{ |
|
yield return switchStatement.Expression; |
|
} |
|
|
|
public override IEnumerable<Expression> VisitForStatement (ForStatement forStatement) |
|
{ |
|
yield return forStatement.Condition; |
|
} |
|
|
|
public override IEnumerable<Expression> VisitDoWhileStatement (DoWhileStatement doWhileStatement) |
|
{ |
|
yield return doWhileStatement.Condition; |
|
} |
|
|
|
public override IEnumerable<Expression> VisitWhileStatement (WhileStatement whileStatement) |
|
{ |
|
yield return whileStatement.Condition; |
|
} |
|
|
|
public override IEnumerable<Expression> VisitForeachStatement (ForeachStatement foreachStatement) |
|
{ |
|
yield return foreachStatement.InExpression; |
|
} |
|
|
|
public override IEnumerable<Expression> VisitExpressionStatement (ExpressionStatement expressionStatement) |
|
{ |
|
yield return expressionStatement.Expression; |
|
} |
|
|
|
public override IEnumerable<Expression> VisitLockStatement (LockStatement lockStatement) |
|
{ |
|
yield return lockStatement.Expression; |
|
} |
|
|
|
public override IEnumerable<Expression> VisitReturnStatement (ReturnStatement returnStatement) |
|
{ |
|
yield return returnStatement.Expression; |
|
} |
|
|
|
public override IEnumerable<Expression> VisitThrowStatement (ThrowStatement throwStatement) |
|
{ |
|
yield return throwStatement.Expression; |
|
} |
|
|
|
public override IEnumerable<Expression> VisitUsingStatement (UsingStatement usingStatement) |
|
{ |
|
var expr = usingStatement.ResourceAcquisition as Expression; |
|
if (expr != null) |
|
return new [] { expr }; |
|
|
|
return usingStatement.ResourceAcquisition.AcceptVisitor (this); |
|
} |
|
|
|
public override IEnumerable<Expression> VisitVariableDeclarationStatement ( |
|
VariableDeclarationStatement variableDeclarationStatement) |
|
{ |
|
return variableDeclarationStatement.Variables.Select (v => v.Initializer); |
|
} |
|
|
|
public override IEnumerable<Expression> VisitYieldReturnStatement (YieldReturnStatement yieldReturnStatement) |
|
{ |
|
yield return yieldReturnStatement.Expression; |
|
} |
|
|
|
} |
|
|
|
class CfgVariableReferenceNodeBuilder |
|
{ |
|
static GetExpressionsVisitor getExpr = new GetExpressionsVisitor (); |
|
|
|
ISet<AstNode> references; |
|
ISet<Statement> refStatements; |
|
CSharpAstResolver resolver; |
|
Dictionary<ControlFlowNode, VariableReferenceNode> nodeDict; |
|
|
|
public VariableReferenceNode Build (ControlFlowNode startNode, ISet<AstNode> references, |
|
ISet<Statement> refStatements, CSharpAstResolver resolver) |
|
{ |
|
this.references = references; |
|
this.refStatements = refStatements; |
|
this.resolver = resolver; |
|
nodeDict = new Dictionary<ControlFlowNode, VariableReferenceNode> (); |
|
return AddNode (startNode); |
|
} |
|
|
|
static bool IsValidControlFlowNode (ControlFlowNode node) |
|
{ |
|
if (node.NextStatement == null) |
|
return false; |
|
if (node.Type == ControlFlowNodeType.LoopCondition) { |
|
if (node.NextStatement is ForeachStatement) |
|
return false; |
|
} else { |
|
if (node.NextStatement is WhileStatement || node.NextStatement is DoWhileStatement || |
|
node.NextStatement is ForStatement) |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
VariableReferenceNode GetStatementEndNode (VariableReferenceNode currentNode, Statement statement) |
|
{ |
|
var expressions = statement.AcceptVisitor (getExpr); |
|
VariableReferenceNode endNode; |
|
ExpressionNodeCreationVisitor.CreateNode (references, resolver, expressions, currentNode, out endNode); |
|
return endNode; |
|
} |
|
|
|
VariableReferenceNode AddNode (ControlFlowNode startNode) |
|
{ |
|
var node = new VariableReferenceNode (); |
|
var cfNode = startNode; |
|
while (true) { |
|
if (nodeDict.ContainsKey (cfNode)) { |
|
node.AddNextNode (nodeDict [cfNode]); |
|
break; |
|
} |
|
// create a new node for fork point |
|
if (cfNode.Incoming.Count > 1 || cfNode.Outgoing.Count > 1) { |
|
nodeDict [cfNode] = node; |
|
|
|
var forkNode = new VariableReferenceNode (); |
|
node.AddNextNode (forkNode); |
|
node = forkNode; |
|
} |
|
nodeDict [cfNode] = node; |
|
|
|
if (IsValidControlFlowNode (cfNode) && refStatements.Contains (cfNode.NextStatement)) |
|
node = GetStatementEndNode (node, cfNode.NextStatement); |
|
|
|
if (cfNode.Outgoing.Count == 1) { |
|
cfNode = cfNode.Outgoing [0].To; |
|
} else { |
|
foreach (var e in cfNode.Outgoing) |
|
node.AddNextNode (AddNode (e.To)); |
|
break; |
|
} |
|
} |
|
return nodeDict [startNode]; |
|
} |
|
} |
|
|
|
class ExpressionNodeCreationVisitor : DepthFirstAstVisitor |
|
{ |
|
VariableReferenceNode startNode; |
|
VariableReferenceNode endNode; |
|
ISet<AstNode> references; |
|
CSharpAstResolver resolver; |
|
|
|
ExpressionNodeCreationVisitor (ISet<AstNode> references, CSharpAstResolver resolver, |
|
VariableReferenceNode startNode) |
|
{ |
|
this.references = references; |
|
this.resolver = resolver; |
|
this.startNode = this.endNode = startNode ?? new VariableReferenceNode (); |
|
} |
|
|
|
public static VariableReferenceNode CreateNode (ISet<AstNode> references, CSharpAstResolver resolver, |
|
params Expression [] expressions) |
|
{ |
|
VariableReferenceNode endNode; |
|
return CreateNode (references, resolver, expressions, null, out endNode); |
|
} |
|
|
|
public static VariableReferenceNode CreateNode (ISet<AstNode> references, CSharpAstResolver resolver, |
|
IEnumerable<Expression> expressions, VariableReferenceNode startNode, out VariableReferenceNode endNode) |
|
{ |
|
startNode = startNode ?? new VariableReferenceNode (); |
|
endNode = startNode; |
|
foreach (var expr in expressions) { |
|
var visitor = CreateVisitor (references, resolver, expr, endNode); |
|
endNode = visitor.endNode; |
|
} |
|
return startNode; |
|
} |
|
|
|
static ExpressionNodeCreationVisitor CreateVisitor (ISet<AstNode> references, CSharpAstResolver resolver, |
|
Expression rootExpr, VariableReferenceNode startNode = null, VariableReferenceNode nextNode = null) |
|
{ |
|
var visitor = new ExpressionNodeCreationVisitor (references, resolver, startNode); |
|
rootExpr.AcceptVisitor (visitor); |
|
if (nextNode != null) |
|
visitor.endNode.AddNextNode (nextNode); |
|
return visitor; |
|
} |
|
|
|
static VariableReferenceNode CreateNode (ISet<AstNode> references, CSharpAstResolver resolver, |
|
Expression rootExpr, VariableReferenceNode startNode = null, VariableReferenceNode nextNode = null) |
|
{ |
|
return CreateVisitor (references, resolver, rootExpr, startNode, nextNode).startNode; |
|
} |
|
|
|
#region Skipped Expressions |
|
|
|
public override void VisitAnonymousMethodExpression (AnonymousMethodExpression anonymousMethodExpression) |
|
{ |
|
} |
|
|
|
public override void VisitLambdaExpression (LambdaExpression lambdaExpression) |
|
{ |
|
} |
|
|
|
public override void VisitBaseReferenceExpression (BaseReferenceExpression baseReferenceExpression) |
|
{ |
|
} |
|
|
|
public override void VisitNullReferenceExpression (NullReferenceExpression nullReferenceExpression) |
|
{ |
|
} |
|
|
|
public override void VisitPrimitiveExpression (PrimitiveExpression primitiveExpression) |
|
{ |
|
} |
|
|
|
public override void VisitSizeOfExpression (SizeOfExpression sizeOfExpression) |
|
{ |
|
} |
|
|
|
public override void VisitThisReferenceExpression (ThisReferenceExpression thisReferenceExpression) |
|
{ |
|
} |
|
|
|
public override void VisitTypeOfExpression (TypeOfExpression typeOfExpression) |
|
{ |
|
} |
|
|
|
public override void VisitTypeReferenceExpression (TypeReferenceExpression typeReferenceExpression) |
|
{ |
|
} |
|
|
|
public override void VisitUndocumentedExpression (UndocumentedExpression undocumentedExpression) |
|
{ |
|
} |
|
|
|
public override void VisitDefaultValueExpression (DefaultValueExpression defaultValueExpression) |
|
{ |
|
} |
|
|
|
public override void VisitEmptyExpression (EmptyExpression emptyExpression) |
|
{ |
|
} |
|
|
|
#endregion |
|
|
|
public override void VisitAssignmentExpression (AssignmentExpression assignmentExpression) |
|
{ |
|
assignmentExpression.Right.AcceptVisitor (this); |
|
assignmentExpression.Left.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitBinaryOperatorExpression (BinaryOperatorExpression binaryOperatorExpression) |
|
{ |
|
binaryOperatorExpression.Left.AcceptVisitor (this); |
|
binaryOperatorExpression.Right.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitCastExpression (CastExpression castExpression) |
|
{ |
|
castExpression.Expression.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitCheckedExpression (CheckedExpression checkedExpression) |
|
{ |
|
checkedExpression.Expression.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitConditionalExpression (ConditionalExpression conditionalExpression) |
|
{ |
|
conditionalExpression.Condition.AcceptVisitor (this); |
|
var resolveResult = resolver.Resolve (conditionalExpression.Condition); |
|
if (resolveResult.ConstantValue is bool) { |
|
if ((bool) resolveResult.ConstantValue) |
|
conditionalExpression.TrueExpression.AcceptVisitor (this); |
|
else |
|
conditionalExpression.FalseExpression.AcceptVisitor (this); |
|
return; |
|
} |
|
var nextEndNode = new VariableReferenceNode (); |
|
var trueNode = CreateNode (references, resolver, conditionalExpression.TrueExpression, null, |
|
nextEndNode); |
|
var falseNode = CreateNode (references, resolver, conditionalExpression.FalseExpression, null, |
|
nextEndNode); |
|
endNode.AddNextNode (trueNode); |
|
endNode.AddNextNode (falseNode); |
|
endNode = nextEndNode; |
|
} |
|
|
|
public override void VisitIdentifierExpression (IdentifierExpression identifierExpression) |
|
{ |
|
if (references.Contains (identifierExpression)) |
|
endNode.References.Add (identifierExpression); |
|
} |
|
|
|
public override void VisitIndexerExpression (IndexerExpression indexerExpression) |
|
{ |
|
indexerExpression.Target.AcceptVisitor (this); |
|
foreach (var arg in indexerExpression.Arguments) |
|
arg.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitInvocationExpression (InvocationExpression invocationExpression) |
|
{ |
|
invocationExpression.Target.AcceptVisitor (this); |
|
var outArguments = new List<Expression> (); |
|
foreach (var arg in invocationExpression.Arguments) { |
|
var directionExpr = arg as DirectionExpression; |
|
if (directionExpr != null && directionExpr.FieldDirection == FieldDirection.Out) { |
|
outArguments.Add (directionExpr); |
|
continue; |
|
} |
|
arg.AcceptVisitor (this); |
|
} |
|
foreach (var arg in outArguments) |
|
arg.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitDirectionExpression (DirectionExpression directionExpression) |
|
{ |
|
directionExpression.Expression.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitMemberReferenceExpression (MemberReferenceExpression memberReferenceExpression) |
|
{ |
|
memberReferenceExpression.Target.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitObjectCreateExpression (ObjectCreateExpression objectCreateExpression) |
|
{ |
|
foreach (var arg in objectCreateExpression.Arguments) |
|
arg.AcceptVisitor (this); |
|
objectCreateExpression.Initializer.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitAnonymousTypeCreateExpression ( |
|
AnonymousTypeCreateExpression anonymousTypeCreateExpression) |
|
{ |
|
foreach (var init in anonymousTypeCreateExpression.Initializers) |
|
init.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitArrayCreateExpression (ArrayCreateExpression arrayCreateExpression) |
|
{ |
|
foreach (var arg in arrayCreateExpression.Arguments) |
|
arg.AcceptVisitor (this); |
|
arrayCreateExpression.Initializer.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitParenthesizedExpression (ParenthesizedExpression parenthesizedExpression) |
|
{ |
|
parenthesizedExpression.Expression.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitPointerReferenceExpression (PointerReferenceExpression pointerReferenceExpression) |
|
{ |
|
pointerReferenceExpression.Target.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitStackAllocExpression (StackAllocExpression stackAllocExpression) |
|
{ |
|
stackAllocExpression.CountExpression.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitUnaryOperatorExpression (UnaryOperatorExpression unaryOperatorExpression) |
|
{ |
|
unaryOperatorExpression.Expression.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitUncheckedExpression (UncheckedExpression uncheckedExpression) |
|
{ |
|
uncheckedExpression.Expression.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitAsExpression (AsExpression asExpression) |
|
{ |
|
asExpression.Expression.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitIsExpression (IsExpression isExpression) |
|
{ |
|
isExpression.Expression.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitArrayInitializerExpression (ArrayInitializerExpression arrayInitializerExpression) |
|
{ |
|
foreach (var element in arrayInitializerExpression.Elements) |
|
element.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitNamedArgumentExpression (NamedArgumentExpression namedArgumentExpression) |
|
{ |
|
namedArgumentExpression.Expression.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitNamedExpression (NamedExpression namedExpression) |
|
{ |
|
namedExpression.Expression.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitQueryExpression (QueryExpression queryExpression) |
|
{ |
|
foreach (var clause in queryExpression.Clauses) |
|
clause.AcceptVisitor (this); |
|
} |
|
|
|
#region Query Clauses |
|
|
|
public override void VisitQueryContinuationClause (QueryContinuationClause queryContinuationClause) |
|
{ |
|
queryContinuationClause.PrecedingQuery.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitQueryFromClause (QueryFromClause queryFromClause) |
|
{ |
|
queryFromClause.Expression.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitQueryJoinClause (QueryJoinClause queryJoinClause) |
|
{ |
|
queryJoinClause.InExpression.AcceptVisitor (this); |
|
} |
|
|
|
public override void VisitQueryLetClause (QueryLetClause queryLetClause) |
|
{ |
|
} |
|
|
|
public override void VisitQueryWhereClause (QueryWhereClause queryWhereClause) |
|
{ |
|
} |
|
|
|
public override void VisitQueryOrderClause (QueryOrderClause queryOrderClause) |
|
{ |
|
} |
|
|
|
public override void VisitQueryOrdering (QueryOrdering queryOrdering) |
|
{ |
|
} |
|
|
|
public override void VisitQuerySelectClause (QuerySelectClause querySelectClause) |
|
{ |
|
} |
|
|
|
public override void VisitQueryGroupClause (QueryGroupClause queryGroupClause) |
|
{ |
|
} |
|
|
|
#endregion |
|
} |
|
} |
|
} |