|
|
|
@ -24,343 +24,216 @@ using System.Threading;
@@ -24,343 +24,216 @@ using System.Threading;
|
|
|
|
|
using ICSharpCode.Decompiler.IL; |
|
|
|
|
using ICSharpCode.NRefactory.CSharp; |
|
|
|
|
using ICSharpCode.NRefactory.CSharp.Analysis; |
|
|
|
|
using ICSharpCode.NRefactory.PatternMatching; |
|
|
|
|
using ICSharpCode.NRefactory.Semantics; |
|
|
|
|
using ICSharpCode.NRefactory.TypeSystem; |
|
|
|
|
using ICSharpCode.NRefactory.Utils; |
|
|
|
|
|
|
|
|
|
namespace ICSharpCode.Decompiler.CSharp.Transforms |
|
|
|
|
{ |
|
|
|
|
/// <summary>
|
|
|
|
|
/// Moves variable declarations to improved positions.
|
|
|
|
|
/// Insert variable declarations.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class DeclareVariables : IAstTransform |
|
|
|
|
{ |
|
|
|
|
sealed class VariableToDeclare |
|
|
|
|
struct InsertionPoint |
|
|
|
|
{ |
|
|
|
|
public AstType Type; |
|
|
|
|
public string Name; |
|
|
|
|
public ILVariable ILVariable; |
|
|
|
|
internal int level; |
|
|
|
|
internal AstNode nextNode; |
|
|
|
|
|
|
|
|
|
public AssignmentExpression ReplacedAssignment; |
|
|
|
|
public Statement InsertionPoint; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
CancellationToken cancellationToken; |
|
|
|
|
List<VariableToDeclare> variablesToDeclare = new List<VariableToDeclare>(); |
|
|
|
|
|
|
|
|
|
public void Run(AstNode rootNode, TransformContext context) |
|
|
|
|
{ |
|
|
|
|
this.cancellationToken = context.CancellationToken; |
|
|
|
|
RunInternal(rootNode, null); |
|
|
|
|
// Declare all the variables at the end, after all the logic has run.
|
|
|
|
|
// This is done so that definite assignment analysis can work on a single representation and doesn't have to be updated
|
|
|
|
|
// when we change the AST.
|
|
|
|
|
foreach (var v in variablesToDeclare) { |
|
|
|
|
if (v.ReplacedAssignment == null) { |
|
|
|
|
BlockStatement block = (BlockStatement)v.InsertionPoint.Parent; |
|
|
|
|
var decl = new VariableDeclarationStatement((AstType)v.Type.Clone(), v.Name); |
|
|
|
|
var ilVar = v.ILVariable; |
|
|
|
|
if (ilVar != null) { |
|
|
|
|
decl.Variables.Single().AddAnnotation(ilVar); |
|
|
|
|
if (ilVar.HasInitialValue && ilVar.Kind == VariableKind.Local) |
|
|
|
|
decl.Variables.Single().Initializer = new DefaultValueExpression((AstType)v.Type.Clone()); |
|
|
|
|
} |
|
|
|
|
block.Statements.InsertBefore(v.InsertionPoint, decl); |
|
|
|
|
} |
|
|
|
|
/// <summary>Go up one level</summary>
|
|
|
|
|
internal InsertionPoint Up() |
|
|
|
|
{ |
|
|
|
|
return new InsertionPoint { |
|
|
|
|
level = level - 1, |
|
|
|
|
nextNode = nextNode.Parent |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
// First do all the insertions, then do all the replacements. This is necessary because a replacement might remove our reference point from the AST.
|
|
|
|
|
foreach (var v in variablesToDeclare) { |
|
|
|
|
if (v.ReplacedAssignment != null) { |
|
|
|
|
// We clone the right expression so that it doesn't get removed from the old ExpressionStatement,
|
|
|
|
|
// which might be still in use by the definite assignment graph.
|
|
|
|
|
VariableInitializer initializer = new VariableInitializer(v.Name, v.ReplacedAssignment.Right.Detach()).CopyAnnotationsFrom(v.ReplacedAssignment).WithAnnotation(v.ILVariable); |
|
|
|
|
VariableDeclarationStatement varDecl = new VariableDeclarationStatement { |
|
|
|
|
Type = (AstType)v.Type.Clone(), |
|
|
|
|
Variables = { initializer } |
|
|
|
|
}; |
|
|
|
|
ExpressionStatement es = v.ReplacedAssignment.Parent as ExpressionStatement; |
|
|
|
|
if (es != null) { |
|
|
|
|
// Note: if this crashes with 'Cannot replace the root node', check whether two variables were assigned the same name
|
|
|
|
|
es.ReplaceWith(varDecl.CopyAnnotationsFrom(es)); |
|
|
|
|
} else { |
|
|
|
|
v.ReplacedAssignment.ReplaceWith(varDecl); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
internal InsertionPoint UpTo(int targetLevel) |
|
|
|
|
{ |
|
|
|
|
InsertionPoint result = this; |
|
|
|
|
while (result.level > targetLevel) { |
|
|
|
|
result.nextNode = result.nextNode.Parent; |
|
|
|
|
result.level -= 1; |
|
|
|
|
} |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
variablesToDeclare = null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void RunInternal(AstNode node, DefiniteAssignmentAnalysis daa) |
|
|
|
|
class VariableToDeclare |
|
|
|
|
{ |
|
|
|
|
BlockStatement block = node as BlockStatement; |
|
|
|
|
if (block != null) { |
|
|
|
|
var variables = block.Statements.TakeWhile(stmt => stmt is VariableDeclarationStatement) |
|
|
|
|
.Cast<VariableDeclarationStatement>().ToList(); |
|
|
|
|
if (variables.Count > 0) { |
|
|
|
|
// remove old variable declarations:
|
|
|
|
|
foreach (VariableDeclarationStatement varDecl in variables) { |
|
|
|
|
Debug.Assert(varDecl.Variables.Single().Initializer.IsNull); |
|
|
|
|
varDecl.Remove(); |
|
|
|
|
} |
|
|
|
|
if (daa == null) { |
|
|
|
|
// If possible, reuse the DefiniteAssignmentAnalysis that was created for the parent block
|
|
|
|
|
daa = new DefiniteAssignmentAnalysis(block, cancellationToken); |
|
|
|
|
} |
|
|
|
|
foreach (VariableDeclarationStatement varDecl in variables) { |
|
|
|
|
VariableInitializer initializer = varDecl.Variables.Single(); |
|
|
|
|
string variableName = initializer.Name; |
|
|
|
|
ILVariable v = initializer.Annotation<ILVariable>(); |
|
|
|
|
bool allowPassIntoLoops = initializer.Annotation<CapturedVariableAnnotation>() == null; |
|
|
|
|
DeclareVariableInBlock(daa, block, varDecl.Type, variableName, v, allowPassIntoLoops); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { |
|
|
|
|
RunInternal(child, daa); |
|
|
|
|
public readonly IType Type; |
|
|
|
|
public readonly string Name; |
|
|
|
|
/// <summary>
|
|
|
|
|
/// Whether the variable needs to be default-initialized.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public readonly bool DefaultInitialization; |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Integer value that can be used to compare to VariableToDeclare instances
|
|
|
|
|
/// to determine which variable was used first in the source code.
|
|
|
|
|
///
|
|
|
|
|
/// Assuming both insertion points are on the same level, the variable
|
|
|
|
|
/// with the lower SourceOrder value has the insertion point that comes
|
|
|
|
|
/// first in the source code.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public int SourceOrder; |
|
|
|
|
|
|
|
|
|
public InsertionPoint InsertionPoint; |
|
|
|
|
|
|
|
|
|
public bool RemovedDueToCollision; |
|
|
|
|
|
|
|
|
|
public VariableToDeclare(IType type, string name, bool defaultInitialization, InsertionPoint insertionPoint, int sourceOrder) |
|
|
|
|
{ |
|
|
|
|
this.Type = type; |
|
|
|
|
this.Name = name; |
|
|
|
|
this.DefaultInitialization = defaultInitialization; |
|
|
|
|
this.InsertionPoint = insertionPoint; |
|
|
|
|
this.SourceOrder = sourceOrder; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DeclareVariableInBlock(DefiniteAssignmentAnalysis daa, BlockStatement block, AstType type, string variableName, ILVariable v, bool allowPassIntoLoops) |
|
|
|
|
{ |
|
|
|
|
// declarationPoint: The point where the variable would be declared, if we decide to declare it in this block
|
|
|
|
|
Statement declarationPoint = null; |
|
|
|
|
// Check whether we can move down the variable into the sub-blocks
|
|
|
|
|
bool canMoveVariableIntoSubBlocks = FindDeclarationPoint(daa, variableName, allowPassIntoLoops, block, out declarationPoint); |
|
|
|
|
if (declarationPoint == null) { |
|
|
|
|
// The variable isn't used at all
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
if (canMoveVariableIntoSubBlocks) { |
|
|
|
|
// Declare the variable within the sub-blocks
|
|
|
|
|
foreach (Statement stmt in block.Statements) { |
|
|
|
|
ForStatement forStmt = stmt as ForStatement; |
|
|
|
|
if (forStmt != null && forStmt.Initializers.Count == 1) { |
|
|
|
|
// handle the special case of moving a variable into the for initializer
|
|
|
|
|
if (TryConvertAssignmentExpressionIntoVariableDeclaration(forStmt.Initializers.Single(), type, variableName)) |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
UsingStatement usingStmt = stmt as UsingStatement; |
|
|
|
|
if (usingStmt != null && usingStmt.ResourceAcquisition is AssignmentExpression) { |
|
|
|
|
// handle the special case of moving a variable into a using statement
|
|
|
|
|
if (TryConvertAssignmentExpressionIntoVariableDeclaration((Expression)usingStmt.ResourceAcquisition, type, variableName)) |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
IfElseStatement ies = stmt as IfElseStatement; |
|
|
|
|
if (ies != null) { |
|
|
|
|
foreach (var child in IfElseChainChildren(ies)) { |
|
|
|
|
BlockStatement subBlock = child as BlockStatement; |
|
|
|
|
if (subBlock != null) |
|
|
|
|
DeclareVariableInBlock(daa, subBlock, type, variableName, v, allowPassIntoLoops); |
|
|
|
|
} |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
foreach (AstNode child in stmt.Children) { |
|
|
|
|
BlockStatement subBlock = child as BlockStatement; |
|
|
|
|
if (subBlock != null) { |
|
|
|
|
DeclareVariableInBlock(daa, subBlock, type, variableName, v, allowPassIntoLoops); |
|
|
|
|
} else if (HasNestedBlocks(child)) { |
|
|
|
|
foreach (BlockStatement nestedSubBlock in child.Children.OfType<BlockStatement>()) { |
|
|
|
|
DeclareVariableInBlock(daa, nestedSubBlock, type, variableName, v, allowPassIntoLoops); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
// Try converting an assignment expression into a VariableDeclarationStatement
|
|
|
|
|
if (!TryConvertAssignmentExpressionIntoVariableDeclaration(declarationPoint, type, variableName)) { |
|
|
|
|
// Declare the variable in front of declarationPoint
|
|
|
|
|
variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ILVariable = v, InsertionPoint = declarationPoint }); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool TryConvertAssignmentExpressionIntoVariableDeclaration(Statement declarationPoint, AstType type, string variableName) |
|
|
|
|
{ |
|
|
|
|
// convert the declarationPoint into a VariableDeclarationStatement
|
|
|
|
|
ExpressionStatement es = declarationPoint as ExpressionStatement; |
|
|
|
|
if (es != null) { |
|
|
|
|
return TryConvertAssignmentExpressionIntoVariableDeclaration(es.Expression, type, variableName); |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
Dictionary<ILVariable, VariableToDeclare> variableDict = new Dictionary<ILVariable, VariableToDeclare>(); |
|
|
|
|
|
|
|
|
|
bool TryConvertAssignmentExpressionIntoVariableDeclaration(Expression expression, AstType type, string variableName) |
|
|
|
|
TransformContext context; |
|
|
|
|
|
|
|
|
|
public void Run(AstNode rootNode, TransformContext context) |
|
|
|
|
{ |
|
|
|
|
AssignmentExpression ae = expression as AssignmentExpression; |
|
|
|
|
if (ae != null && ae.Operator == AssignmentOperatorType.Assign) { |
|
|
|
|
IdentifierExpression ident = ae.Left as IdentifierExpression; |
|
|
|
|
if (ident != null && ident.Identifier == variableName) { |
|
|
|
|
variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ILVariable = ident.Annotation<ILVariable>(), ReplacedAssignment = ae }); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
try { |
|
|
|
|
this.context = context; |
|
|
|
|
FindInsertionPoints(rootNode, 0); |
|
|
|
|
ResolveOverlap(); |
|
|
|
|
InsertVariableDeclarations(); |
|
|
|
|
} finally { |
|
|
|
|
variableDict.Clear(); |
|
|
|
|
this.context = null; |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#region FindInsertionPoints
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Finds the declaration point for the variable within the specified block.
|
|
|
|
|
/// Finds insertion points for all variables used within `node`
|
|
|
|
|
/// and adds them to the variableDict.
|
|
|
|
|
///
|
|
|
|
|
/// `level` == nesting depth of `node` within root node.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="daa">
|
|
|
|
|
/// Definite assignment analysis, must be prepared for 'block' or one of its parents.
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="varDecl">The variable to declare</param>
|
|
|
|
|
/// <param name="block">The block in which the variable should be declared</param>
|
|
|
|
|
/// <param name="declarationPoint">
|
|
|
|
|
/// Output parameter: the first statement within 'block' where the variable needs to be declared.
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <returns>
|
|
|
|
|
/// Returns whether it is possible to move the variable declaration into sub-blocks.
|
|
|
|
|
/// </returns>
|
|
|
|
|
public static bool FindDeclarationPoint(DefiniteAssignmentAnalysis daa, VariableDeclarationStatement varDecl, BlockStatement block, out Statement declarationPoint) |
|
|
|
|
{ |
|
|
|
|
string variableName = varDecl.Variables.Single().Name; |
|
|
|
|
bool allowPassIntoLoops = varDecl.Variables.Single().Annotation<CapturedVariableAnnotation>() == null; |
|
|
|
|
return FindDeclarationPoint(daa, variableName, allowPassIntoLoops, block, out declarationPoint); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool FindDeclarationPoint(DefiniteAssignmentAnalysis daa, string variableName, bool allowPassIntoLoops, BlockStatement block, out Statement declarationPoint) |
|
|
|
|
void FindInsertionPoints(AstNode node, int nodeLevel) |
|
|
|
|
{ |
|
|
|
|
// declarationPoint: The point where the variable would be declared, if we decide to declare it in this block
|
|
|
|
|
declarationPoint = null; |
|
|
|
|
foreach (Statement stmt in block.Statements) { |
|
|
|
|
if (UsesVariable(stmt, variableName)) { |
|
|
|
|
if (declarationPoint == null) |
|
|
|
|
declarationPoint = stmt; |
|
|
|
|
if (!CanMoveVariableUseIntoSubBlock(stmt, variableName, allowPassIntoLoops)) { |
|
|
|
|
// If it's not possible to move the variable use into a nested block,
|
|
|
|
|
// we need to declare the variable in this block
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
// If we can move the variable into the sub-block, we need to ensure that the remaining code
|
|
|
|
|
// does not use the value that was assigned by the first sub-block
|
|
|
|
|
Statement nextStatement = stmt.GetNextStatement(); |
|
|
|
|
if (nextStatement != null) { |
|
|
|
|
// Analyze the range from the next statement to the end of the block
|
|
|
|
|
daa.SetAnalyzedRange(nextStatement, block); |
|
|
|
|
daa.Analyze(variableName); |
|
|
|
|
if (daa.UnassignedVariableUses.Count > 0) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { |
|
|
|
|
FindInsertionPoints(child, nodeLevel + 1); |
|
|
|
|
} |
|
|
|
|
var identExpr = node as IdentifierExpression; |
|
|
|
|
if (identExpr != null) { |
|
|
|
|
var rr = identExpr.GetResolveResult() as ILVariableResolveResult; |
|
|
|
|
if (rr != null && rr.Variable.Kind != VariableKind.Parameter && rr.Variable.Kind != VariableKind.Exception) { |
|
|
|
|
var newPoint = new InsertionPoint { level = nodeLevel, nextNode = identExpr }; |
|
|
|
|
VariableToDeclare v; |
|
|
|
|
if (variableDict.TryGetValue(rr.Variable, out v)) { |
|
|
|
|
v.InsertionPoint = FindCommonParent(v.InsertionPoint, newPoint); |
|
|
|
|
} else { |
|
|
|
|
v = new VariableToDeclare( |
|
|
|
|
rr.Variable.Type, rr.Variable.Name, rr.Variable.HasInitialValue, |
|
|
|
|
newPoint, sourceOrder: variableDict.Count); |
|
|
|
|
variableDict.Add(rr.Variable, v); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Finds an insertion point in a common parent instruction.
|
|
|
|
|
/// </summary>
|
|
|
|
|
InsertionPoint FindCommonParent(InsertionPoint oldPoint, InsertionPoint newPoint) |
|
|
|
|
{ |
|
|
|
|
// First ensure we're looking at nodes on the same level:
|
|
|
|
|
oldPoint = oldPoint.UpTo(newPoint.level); |
|
|
|
|
newPoint = newPoint.UpTo(oldPoint.level); |
|
|
|
|
Debug.Assert(newPoint.level == oldPoint.level); |
|
|
|
|
// Then go up the tree until both points share the same parent:
|
|
|
|
|
while (oldPoint.nextNode.Parent != newPoint.nextNode.Parent) { |
|
|
|
|
oldPoint = oldPoint.Up(); |
|
|
|
|
newPoint = newPoint.Up(); |
|
|
|
|
} |
|
|
|
|
// return oldPoint as that one comes first in the source code
|
|
|
|
|
return oldPoint; |
|
|
|
|
} |
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
static bool CanMoveVariableUseIntoSubBlock(Statement stmt, string variableName, bool allowPassIntoLoops) |
|
|
|
|
void ResolveOverlap() |
|
|
|
|
{ |
|
|
|
|
if (!allowPassIntoLoops && (stmt is ForStatement || stmt is ForeachStatement || stmt is DoWhileStatement || stmt is WhileStatement)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
ForStatement forStatement = stmt as ForStatement; |
|
|
|
|
if (forStatement != null && forStatement.Initializers.Count == 1) { |
|
|
|
|
// for-statement is special case: we can move variable declarations into the initializer
|
|
|
|
|
ExpressionStatement es = forStatement.Initializers.Single() as ExpressionStatement; |
|
|
|
|
if (es != null) { |
|
|
|
|
AssignmentExpression ae = es.Expression as AssignmentExpression; |
|
|
|
|
if (ae != null && ae.Operator == AssignmentOperatorType.Assign) { |
|
|
|
|
IdentifierExpression ident = ae.Left as IdentifierExpression; |
|
|
|
|
if (ident != null && ident.Identifier == variableName) { |
|
|
|
|
return !UsesVariable(ae.Right, variableName); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
UsingStatement usingStatement = stmt as UsingStatement; |
|
|
|
|
if (usingStatement != null) { |
|
|
|
|
// using-statement is special case: we can move variable declarations into the initializer
|
|
|
|
|
AssignmentExpression ae = usingStatement.ResourceAcquisition as AssignmentExpression; |
|
|
|
|
if (ae != null && ae.Operator == AssignmentOperatorType.Assign) { |
|
|
|
|
IdentifierExpression ident = ae.Left as IdentifierExpression; |
|
|
|
|
if (ident != null && ident.Identifier == variableName) { |
|
|
|
|
return !UsesVariable(ae.Right, variableName); |
|
|
|
|
} |
|
|
|
|
var multiDict = new MultiDictionary<string, VariableToDeclare>(); |
|
|
|
|
foreach (var v in variableDict.Values) { |
|
|
|
|
// Go up to the next BlockStatement (we can't add variable declarations anywhere else in the AST)
|
|
|
|
|
while (!(v.InsertionPoint.nextNode.Parent is BlockStatement)) { |
|
|
|
|
v.InsertionPoint = v.InsertionPoint.Up(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IfElseStatement ies = stmt as IfElseStatement; |
|
|
|
|
if (ies != null) { |
|
|
|
|
foreach (var child in IfElseChainChildren(ies)) { |
|
|
|
|
if (!(child is BlockStatement) && UsesVariable(child, variableName)) |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// We can move the variable into a sub-block only if the variable is used in only that sub-block (and not in expressions such as the loop condition)
|
|
|
|
|
for (AstNode child = stmt.FirstChild; child != null; child = child.NextSibling) { |
|
|
|
|
if (!(child is BlockStatement) && UsesVariable(child, variableName)) { |
|
|
|
|
if (HasNestedBlocks(child)) { |
|
|
|
|
// catch clauses/switch sections can contain nested blocks
|
|
|
|
|
for (AstNode grandchild = child.FirstChild; grandchild != null; grandchild = grandchild.NextSibling) { |
|
|
|
|
if (!(grandchild is BlockStatement) && UsesVariable(grandchild, variableName)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
// Go through all potentially colliding variables:
|
|
|
|
|
foreach (var prev in multiDict[v.Name]) { |
|
|
|
|
if (prev.RemovedDueToCollision) |
|
|
|
|
continue; |
|
|
|
|
// Go up until both nodes are on the same level:
|
|
|
|
|
InsertionPoint point1 = prev.InsertionPoint.UpTo(v.InsertionPoint.level); |
|
|
|
|
InsertionPoint point2 = v.InsertionPoint.UpTo(prev.InsertionPoint.level); |
|
|
|
|
if (point1.nextNode.Parent == point2.nextNode.Parent) { |
|
|
|
|
// We found a collision!
|
|
|
|
|
prev.RemovedDueToCollision = true; |
|
|
|
|
// Continue checking other entries in multiDict against the new position of `v`.
|
|
|
|
|
if (prev.SourceOrder < v.SourceOrder) { |
|
|
|
|
// If we switch v's insertion point to prev's insertion point,
|
|
|
|
|
// we also need to copy prev's SourceOrder value.
|
|
|
|
|
v.InsertionPoint = point1; |
|
|
|
|
v.SourceOrder = prev.SourceOrder; |
|
|
|
|
} else { |
|
|
|
|
v.InsertionPoint = point2; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
return false; |
|
|
|
|
// I think we don't need to re-check the dict entries that we already checked earlier,
|
|
|
|
|
// because the new v.InsertionPoint only collides with another point x if either
|
|
|
|
|
// the old v.InsertionPoint or the old prev.InsertionPoint already collided with x.
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
multiDict.Add(v.Name, v); |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static IEnumerable<AstNode> IfElseChainChildren(IfElseStatement ies) |
|
|
|
|
{ |
|
|
|
|
IfElseStatement prev; |
|
|
|
|
do { |
|
|
|
|
yield return ies.Condition; |
|
|
|
|
yield return ies.TrueStatement; |
|
|
|
|
prev = ies; |
|
|
|
|
ies = ies.FalseStatement as IfElseStatement; |
|
|
|
|
} while (ies != null); |
|
|
|
|
if (!prev.FalseStatement.IsNull) |
|
|
|
|
yield return prev.FalseStatement; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool HasNestedBlocks(AstNode node) |
|
|
|
|
void InsertVariableDeclarations() |
|
|
|
|
{ |
|
|
|
|
return node is CatchClause || node is NRefactory.CSharp.SwitchSection; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool UsesVariable(AstNode node, string variableName) |
|
|
|
|
{ |
|
|
|
|
IdentifierExpression ie = node as IdentifierExpression; |
|
|
|
|
if (ie != null && ie.Identifier == variableName) |
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
FixedStatement fixedStatement = node as FixedStatement; |
|
|
|
|
if (fixedStatement != null) { |
|
|
|
|
foreach (VariableInitializer v in fixedStatement.Variables) { |
|
|
|
|
if (v.Name == variableName) |
|
|
|
|
return false; // no need to introduce the variable here
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ForeachStatement foreachStatement = node as ForeachStatement; |
|
|
|
|
if (foreachStatement != null) { |
|
|
|
|
if (foreachStatement.VariableName == variableName) |
|
|
|
|
return false; // no need to introduce the variable here
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
UsingStatement usingStatement = node as UsingStatement; |
|
|
|
|
if (usingStatement != null) { |
|
|
|
|
VariableDeclarationStatement varDecl = usingStatement.ResourceAcquisition as VariableDeclarationStatement; |
|
|
|
|
if (varDecl != null) { |
|
|
|
|
foreach (VariableInitializer v in varDecl.Variables) { |
|
|
|
|
if (v.Name == variableName) |
|
|
|
|
return false; // no need to introduce the variable here
|
|
|
|
|
var replacements = new List<KeyValuePair<AstNode, AstNode>>(); |
|
|
|
|
foreach (var v in variableDict.Values) { |
|
|
|
|
if (v.RemovedDueToCollision) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
AstType type = context.TypeSystemAstBuilder.ConvertType(v.Type); |
|
|
|
|
|
|
|
|
|
var boe = v.InsertionPoint.nextNode as BinaryOperatorExpression; |
|
|
|
|
if (boe != null && boe.Left.IsMatch(new IdentifierExpression(v.Name))) { |
|
|
|
|
|
|
|
|
|
var vds = new VariableDeclarationStatement(type, v.Name, boe.Right.Detach()); |
|
|
|
|
var init = vds.Variables.Single(); |
|
|
|
|
init.AddAnnotation(boe.Left.GetResolveResult()); |
|
|
|
|
foreach (object annotation in boe.Left.Annotations.Concat(boe.Annotations)) { |
|
|
|
|
if (!(annotation is ResolveResult)) { |
|
|
|
|
init.AddAnnotation(annotation); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
replacements.Add(new KeyValuePair<AstNode, AstNode>(v.InsertionPoint.nextNode, vds)); |
|
|
|
|
} else { |
|
|
|
|
Expression initializer = null; |
|
|
|
|
if (v.DefaultInitialization) { |
|
|
|
|
initializer = new DefaultValueExpression(type.Clone()); |
|
|
|
|
} |
|
|
|
|
v.InsertionPoint.nextNode.Parent.InsertChildBefore( |
|
|
|
|
v.InsertionPoint.nextNode, |
|
|
|
|
new VariableDeclarationStatement(type, v.Name, initializer), |
|
|
|
|
BlockStatement.StatementRole); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
CatchClause catchClause = node as CatchClause; |
|
|
|
|
if (catchClause != null && catchClause.VariableName == variableName) { |
|
|
|
|
return false; // no need to introduce the variable here
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { |
|
|
|
|
if (UsesVariable(child, variableName)) |
|
|
|
|
return true; |
|
|
|
|
// perform replacements at end, so that we don't replace a node while it is still referenced by a VariableToDeclare
|
|
|
|
|
foreach (var pair in replacements) { |
|
|
|
|
pair.Key.ReplaceWith(pair.Value); |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|