Browse Source

Don't introduce foreach when doing so would create a conflict with another variable of the same name as the loop variable.

pull/832/head
Daniel Grunwald 8 years ago
parent
commit
cad933184a
  1. 6
      ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs
  2. 45
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  3. 16
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
  4. 37
      ICSharpCode.Decompiler/Tests/TestCases/Pretty/Loops.cs

6
ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs

@ -36,6 +36,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
currentMethod = context.DecompiledMember as IMethod; currentMethod = context.DecompiledMember as IMethod;
} }
protected void Uninitialize()
{
currentTypeDefinition = null;
currentMethod = null;
}
public override TResult VisitTypeDeclaration(TypeDeclaration typeDeclaration) public override TResult VisitTypeDeclaration(TypeDeclaration typeDeclaration)
{ {
ITypeDefinition oldType = currentTypeDefinition; ITypeDefinition oldType = currentTypeDefinition;

45
ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs

@ -37,6 +37,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// Represents a position immediately before nextNode. /// Represents a position immediately before nextNode.
/// nextNode is either an ExpressionStatement in a BlockStatement, or an initializer in a for-loop. /// nextNode is either an ExpressionStatement in a BlockStatement, or an initializer in a for-loop.
/// </summary> /// </summary>
[DebuggerDisplay("level = {level}, nextNode = {nextNode}")]
struct InsertionPoint struct InsertionPoint
{ {
/// <summary> /// <summary>
@ -66,6 +67,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} }
} }
[DebuggerDisplay("VariableToDeclare(Name={Name})")]
class VariableToDeclare class VariableToDeclare
{ {
public readonly IType Type; public readonly IType Type;
@ -88,7 +90,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public InsertionPoint InsertionPoint; public InsertionPoint InsertionPoint;
public bool RemovedDueToCollision; public VariableToDeclare ReplacementDueToCollision;
public bool RemovedDueToCollision => ReplacementDueToCollision != null;
public VariableToDeclare(IType type, string name, bool defaultInitialization, InsertionPoint insertionPoint, int sourceOrder) public VariableToDeclare(IType type, string name, bool defaultInitialization, InsertionPoint insertionPoint, int sourceOrder)
{ {
@ -102,22 +105,48 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
readonly Dictionary<ILVariable, VariableToDeclare> variableDict = new Dictionary<ILVariable, VariableToDeclare>(); readonly Dictionary<ILVariable, VariableToDeclare> variableDict = new Dictionary<ILVariable, VariableToDeclare>();
TransformContext context;
public void Run(AstNode rootNode, TransformContext context) public void Run(AstNode rootNode, TransformContext context)
{ {
try { try {
this.context = context; variableDict.Clear();
EnsureExpressionStatementsAreValid(rootNode); EnsureExpressionStatementsAreValid(rootNode);
FindInsertionPoints(rootNode, 0); FindInsertionPoints(rootNode, 0);
ResolveCollisions(); ResolveCollisions();
InsertVariableDeclarations(); InsertVariableDeclarations(context);
} finally { } finally {
variableDict.Clear(); variableDict.Clear();
this.context = null;
} }
} }
/// <summary>
/// Analyze the input AST (containing undeclared variables)
/// for where those variables would be declared by this transform.
/// Analysis does not modify the AST.
/// </summary>
public void Analyze(AstNode rootNode)
{
variableDict.Clear();
FindInsertionPoints(rootNode, 0);
ResolveCollisions();
}
/// <summary>
/// Get the position where the declaration for the variable will be inserted.
/// </summary>
public AstNode GetDeclarationPoint(ILVariable variable)
{
VariableToDeclare v = variableDict[variable];
while (v.ReplacementDueToCollision != null) {
v = v.ReplacementDueToCollision;
}
return v.InsertionPoint.nextNode;
}
public void ClearAnalysisResults()
{
variableDict.Clear();
}
#region EnsureExpressionStatementsAreValid #region EnsureExpressionStatementsAreValid
void EnsureExpressionStatementsAreValid(AstNode rootNode) void EnsureExpressionStatementsAreValid(AstNode rootNode)
{ {
@ -297,7 +326,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
Debug.Assert(point1.level == point2.level); Debug.Assert(point1.level == point2.level);
if (point1.nextNode.Parent == point2.nextNode.Parent) { if (point1.nextNode.Parent == point2.nextNode.Parent) {
// We found a collision! // We found a collision!
prev.RemovedDueToCollision = true; prev.ReplacementDueToCollision = v;
// Continue checking other entries in multiDict against the new position of `v`. // Continue checking other entries in multiDict against the new position of `v`.
if (prev.SourceOrder < v.SourceOrder) { if (prev.SourceOrder < v.SourceOrder) {
// If we switch v's insertion point to prev's insertion point, // If we switch v's insertion point to prev's insertion point,
@ -318,7 +347,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} }
} }
void InsertVariableDeclarations() void InsertVariableDeclarations(TransformContext context)
{ {
var replacements = new List<KeyValuePair<AstNode, AstNode>>(); var replacements = new List<KeyValuePair<AstNode, AstNode>>();
foreach (var p in variableDict) { foreach (var p in variableDict) {

16
ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

@ -35,13 +35,23 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// </summary> /// </summary>
public sealed class PatternStatementTransform : ContextTrackingVisitor<AstNode>, IAstTransform public sealed class PatternStatementTransform : ContextTrackingVisitor<AstNode>, IAstTransform
{ {
readonly DeclareVariables declareVariables = new DeclareVariables();
TransformContext context; TransformContext context;
public void Run(AstNode rootNode, TransformContext context) public void Run(AstNode rootNode, TransformContext context)
{ {
if (this.context != null)
throw new InvalidOperationException("Reentrancy in PatternStatementTransform.Run?");
try {
this.context = context; this.context = context;
base.Initialize(context); base.Initialize(context);
declareVariables.Analyze(rootNode);
rootNode.AcceptVisitor(this); rootNode.AcceptVisitor(this);
} finally {
this.context = null;
base.Uninitialize();
declareVariables.ClearAnalysisResults();
}
} }
#region Visitor Overrides #region Visitor Overrides
@ -434,7 +444,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return foreachStatement; return foreachStatement;
} }
static bool VariableCanBeDeclaredInLoop(IL.ILVariable itemVar, WhileStatement loop) bool VariableCanBeDeclaredInLoop(IL.ILVariable itemVar, WhileStatement loop)
{ {
if (itemVar == null || !(itemVar.Kind == IL.VariableKind.Local || itemVar.Kind == IL.VariableKind.StackSlot)) { if (itemVar == null || !(itemVar.Kind == IL.VariableKind.Local || itemVar.Kind == IL.VariableKind.StackSlot)) {
// only locals/temporaries can be converted into foreach loop variable // only locals/temporaries can be converted into foreach loop variable
@ -457,7 +467,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
// captured variables cannot be declared in the loop unless the loop is their capture scope // captured variables cannot be declared in the loop unless the loop is their capture scope
return false; return false;
} }
return true;
AstNode declPoint = declareVariables.GetDeclarationPoint(itemVar);
return declPoint.Ancestors.Contains(loop);
} }
static bool AddressUsedForSingleCall(IL.ILVariable v, IL.BlockContainer loop) static bool AddressUsedForSingleCall(IL.ILVariable v, IL.BlockContainer loop)

37
ICSharpCode.Decompiler/Tests/TestCases/Pretty/Loops.cs

@ -197,5 +197,42 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
Console.WriteLine("End of method"); Console.WriteLine("End of method");
} }
public void DoubleForEachWithSameVariable(IEnumerable<string> enumerable)
{
foreach (string current in enumerable) {
current.ToLower();
}
foreach (string current in enumerable) {
current.ToLower();
}
}
static void ForeachExceptForNameCollision(IEnumerable<int> inputs)
{
Console.WriteLine("ForeachWithNameCollision");
int input;
using (var enumerator = inputs.GetEnumerator()) {
while (enumerator.MoveNext()) {
input = enumerator.Current;
Console.WriteLine(input);
}
}
input = 1;
Console.WriteLine(input);
}
static void ForeachExceptForContinuedUse(IEnumerable<int> inputs)
{
Console.WriteLine("ForeachExceptForContinuedUse");
int input = 0;
using (var enumerator = inputs.GetEnumerator()) {
while (enumerator.MoveNext()) {
input = enumerator.Current;
Console.WriteLine(input);
}
}
Console.WriteLine("Last: " + input);
}
} }
} }

Loading…
Cancel
Save