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. 49
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  3. 22
      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 @@ -36,6 +36,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
currentMethod = context.DecompiledMember as IMethod;
}
protected void Uninitialize()
{
currentTypeDefinition = null;
currentMethod = null;
}
public override TResult VisitTypeDeclaration(TypeDeclaration typeDeclaration)
{
ITypeDefinition oldType = currentTypeDefinition;

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

@ -37,6 +37,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -37,6 +37,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// Represents a position immediately before nextNode.
/// nextNode is either an ExpressionStatement in a BlockStatement, or an initializer in a for-loop.
/// </summary>
[DebuggerDisplay("level = {level}, nextNode = {nextNode}")]
struct InsertionPoint
{
/// <summary>
@ -66,6 +67,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -66,6 +67,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
}
[DebuggerDisplay("VariableToDeclare(Name={Name})")]
class VariableToDeclare
{
public readonly IType Type;
@ -87,9 +89,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -87,9 +89,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public int SourceOrder;
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)
{
this.Type = type;
@ -102,22 +105,48 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -102,22 +105,48 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
readonly Dictionary<ILVariable, VariableToDeclare> variableDict = new Dictionary<ILVariable, VariableToDeclare>();
TransformContext context;
public void Run(AstNode rootNode, TransformContext context)
{
try {
this.context = context;
variableDict.Clear();
EnsureExpressionStatementsAreValid(rootNode);
FindInsertionPoints(rootNode, 0);
ResolveCollisions();
InsertVariableDeclarations();
InsertVariableDeclarations(context);
} finally {
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
void EnsureExpressionStatementsAreValid(AstNode rootNode)
{
@ -297,7 +326,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -297,7 +326,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
Debug.Assert(point1.level == point2.level);
if (point1.nextNode.Parent == point2.nextNode.Parent) {
// We found a collision!
prev.RemovedDueToCollision = true;
prev.ReplacementDueToCollision = v;
// 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,
@ -318,7 +347,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -318,7 +347,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
}
void InsertVariableDeclarations()
void InsertVariableDeclarations(TransformContext context)
{
var replacements = new List<KeyValuePair<AstNode, AstNode>>();
foreach (var p in variableDict) {

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

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