Browse Source

Improved variable placement.

The variable placement step now happens much later in the decompiler pipeline, which simplifies some AST transforms.
pull/100/head
Daniel Grunwald 15 years ago
parent
commit
c7bbdcd0cb
  1. 8
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 91
      ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs
  3. 255
      ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs
  4. 27
      ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
  5. 2
      ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
  6. 190
      ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  7. 1
      ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs
  8. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

8
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -22,7 +22,6 @@ namespace ICSharpCode.Decompiler.Ast
TypeSystem typeSystem; TypeSystem typeSystem;
DecompilerContext context; DecompilerContext context;
HashSet<ILVariable> localVariablesToDefine = new HashSet<ILVariable>(); // local variables that are missing a definition HashSet<ILVariable> localVariablesToDefine = new HashSet<ILVariable>(); // local variables that are missing a definition
HashSet<ILVariable> implicitlyDefinedVariables = new HashSet<ILVariable>(); // local variables that are implicitly defined (e.g. catch handler)
/// <summary> /// <summary>
/// Creates the body for the method definition. /// Creates the body for the method definition.
@ -89,8 +88,9 @@ namespace ICSharpCode.Decompiler.Ast
context.CancellationToken.ThrowIfCancellationRequested(); context.CancellationToken.ThrowIfCancellationRequested();
Ast.BlockStatement astBlock = TransformBlock(ilMethod); Ast.BlockStatement astBlock = TransformBlock(ilMethod);
CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments
foreach (ILVariable v in localVariablesToDefine.Except(implicitlyDefinedVariables)) { foreach (ILVariable v in localVariablesToDefine) {
DeclareVariableInSmallestScope.DeclareVariable(astBlock, AstBuilder.ConvertType(v.Type), v.Name); var newVarDecl = new VariableDeclarationStatement(AstBuilder.ConvertType(v.Type), v.Name);
astBlock.Statements.InsertAfter(null, newVarDecl);
} }
return astBlock; return astBlock;
@ -158,8 +158,6 @@ namespace ICSharpCode.Decompiler.Ast
var tryCatchStmt = new Ast.TryCatchStatement(); var tryCatchStmt = new Ast.TryCatchStatement();
tryCatchStmt.TryBlock = TransformBlock(tryCatchNode.TryBlock); tryCatchStmt.TryBlock = TransformBlock(tryCatchNode.TryBlock);
foreach (var catchClause in tryCatchNode.CatchBlocks) { foreach (var catchClause in tryCatchNode.CatchBlocks) {
if (catchClause.ExceptionVariable != null)
implicitlyDefinedVariables.Add(catchClause.ExceptionVariable);
tryCatchStmt.CatchClauses.Add( tryCatchStmt.CatchClauses.Add(
new Ast.CatchClause { new Ast.CatchClause {
Type = AstBuilder.ConvertType(catchClause.ExceptionType), Type = AstBuilder.ConvertType(catchClause.ExceptionType),

91
ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs

@ -1,91 +0,0 @@
// 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.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.PatternMatching;
namespace ICSharpCode.Decompiler.Ast
{
/// <summary>
/// Helper class for declaring variables.
/// </summary>
public static class DeclareVariableInSmallestScope
{
static readonly ExpressionStatement assignmentPattern = new ExpressionStatement(
new AssignmentExpression(
new NamedNode("ident", new IdentifierExpression()),
new AnyNode("init")
));
/// <summary>
/// Declares a variable in the smallest required scope.
/// </summary>
/// <param name="node">The root of the subtree being searched for the best insertion position</param>
/// <param name="type">The type of the new variable</param>
/// <param name="name">The name of the new variable</param>
/// <param name="allowPassIntoLoops">Whether the variable is allowed to be placed inside a loop</param>
public static VariableDeclarationStatement DeclareVariable(AstNode node, AstType type, string name, bool allowPassIntoLoops = true)
{
VariableDeclarationStatement result = null;
AstNode pos = FindInsertPos(node, name, allowPassIntoLoops);
if (pos != null) {
Match m = assignmentPattern.Match(pos);
if (m != null && m.Get<IdentifierExpression>("ident").Single().Identifier == name) {
result = new VariableDeclarationStatement(type, name, m.Get<Expression>("init").Single().Detach());
result.Variables.Single().CopyAnnotationsFrom(((ExpressionStatement)pos).Expression);
result.CopyAnnotationsFrom(pos);
pos.ReplaceWith(result);
} else {
result = new VariableDeclarationStatement(type, name);
pos.Parent.InsertChildBefore(pos, result, BlockStatement.StatementRole);
}
}
return result;
}
static AstNode FindInsertPos(AstNode node, string name, bool allowPassIntoLoops)
{
AstNode pos = null;
AstNode withinPos = null;
while (node != null) {
IdentifierExpression ident = node as IdentifierExpression;
if (ident != null && ident.Identifier == name && ident.TypeArguments.Count == 0)
return node;
FixedStatement fixedStatement = node as FixedStatement;
if (fixedStatement != null) {
foreach (VariableInitializer v in fixedStatement.Variables) {
if (v.Name == name)
return null; // no need to introduce the variable here
}
}
AstNode withinCurrent = FindInsertPos(node.FirstChild, name, allowPassIntoLoops);
if (withinCurrent != null) {
if (pos == null) {
pos = node;
withinPos = withinCurrent;
} else {
return pos;
}
}
node = node.NextSibling;
}
if (withinPos != null && withinPos.Role == BlockStatement.StatementRole && AllowPassInto(pos, allowPassIntoLoops))
return withinPos;
else
return pos;
}
static bool AllowPassInto(AstNode node, bool allowPassIntoLoops)
{
if (node is AnonymousMethodExpression || node is LambdaExpression)
return false;
if (node is ForStatement || node is ForeachStatement || node is DoWhileStatement || node is WhileStatement)
return allowPassIntoLoops;
return true;
}
}
}

255
ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs

@ -0,0 +1,255 @@
// 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.Diagnostics;
using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.Analysis;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Moves variable declarations to improved positions.
/// </summary>
public class DeclareVariables : IAstTransform
{
sealed class DeclaredVariableAnnotation {
public readonly ExpressionStatement OriginalAssignmentStatement;
public DeclaredVariableAnnotation(ExpressionStatement originalAssignmentStatement)
{
this.OriginalAssignmentStatement = originalAssignmentStatement;
}
}
static readonly DeclaredVariableAnnotation declaredVariableAnnotation = new DeclaredVariableAnnotation(null);
readonly CancellationToken cancellationToken;
public DeclareVariables(DecompilerContext context)
{
this.cancellationToken = context.CancellationToken;
}
public void Run(AstNode node)
{
Run(node, null);
}
void Run(AstNode node, DefiniteAssignmentAnalysis daa)
{
BlockStatement block = node as BlockStatement;
if (block != null) {
var variables = block.Statements.TakeWhile(stmt => stmt is VariableDeclarationStatement
&& stmt.Annotation<DeclaredVariableAnnotation>() == null)
.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) {
string variableName = varDecl.Variables.Single().Name;
bool allowPassIntoLoops = varDecl.Variables.Single().Annotation<DelegateConstruction.CapturedVariableAnnotation>() == null;
DeclareVariableInBlock(daa, block, varDecl.Type, variableName, allowPassIntoLoops);
}
}
}
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
Run(child, daa);
}
}
void DeclareVariableInBlock(DefiniteAssignmentAnalysis daa, BlockStatement block, AstType type, string variableName, 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 ok = true;
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
ok = false;
break;
}
// 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 assigend by the first sub-block
Statement nextStatement = stmt.NextStatement;
// The next statement might be a variable declaration that we inserted, and thus does not exist
// in the definite assignment graph. Thus we need to look up the corresponding instruction
// prior to the introduction of the VariableDeclarationStatement.
while (nextStatement is VariableDeclarationStatement) {
DeclaredVariableAnnotation annotation = nextStatement.Annotation<DeclaredVariableAnnotation>();
if (annotation == null)
break;
if (annotation.OriginalAssignmentStatement != null) {
nextStatement = annotation.OriginalAssignmentStatement;
break;
}
nextStatement = nextStatement.NextStatement;
}
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) {
ok = false;
break;
}
}
}
}
if (declarationPoint == null) {
// The variable isn't used at all
return;
}
if (ok) {
// Declare the variable within the sub-blocks
foreach (Statement stmt in block.Statements) {
foreach (BlockStatement subBlock in stmt.Children.OfType<BlockStatement>()) {
DeclareVariableInBlock(daa, subBlock, type, variableName, allowPassIntoLoops);
}
}
} else {
// Try converting an assignment expression into a VariableDeclarationStatement
ExpressionStatement es = declarationPoint 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) {
// convert the declarationPoint into a VariableDeclarationStatement
declarationPoint.ReplaceWith(
new VariableDeclarationStatement {
Type = (AstType)type.Clone(),
Variables = {
new VariableInitializer(variableName, ae.Right.Detach()).CopyAnnotationsFrom(ae)
}
}.CopyAnnotationsFrom(es).WithAnnotation(new DeclaredVariableAnnotation(es)));
return;
}
}
}
// Declare the variable in front of declarationPoint
block.Statements.InsertBefore(
declarationPoint,
new VariableDeclarationStatement((AstType)type.Clone(), variableName)
.WithAnnotation(declaredVariableAnnotation));
}
}
bool CanMoveVariableUseIntoSubBlock(Statement stmt, string variableName, bool allowPassIntoLoops)
{
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);
}
}
}
}
// We can move the variable into a sub-block only if the variable is used in only that sub-block
for (AstNode child = stmt.FirstChild; child != null; child = child.NextSibling) {
if (!(child is BlockStatement) && UsesVariable(child, variableName))
return false;
}
return true;
}
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
}
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
if (UsesVariable(child, variableName))
return true;
}
return false;
}
#region FindInsertPos
static AstNode FindInsertPos(AstNode node, string name, bool allowPassIntoLoops)
{
AstNode pos = null;
AstNode withinPos = null;
while (node != null) {
IdentifierExpression ident = node as IdentifierExpression;
if (ident != null && ident.Identifier == name && ident.TypeArguments.Count == 0)
return node;
FixedStatement fixedStatement = node as FixedStatement;
if (fixedStatement != null) {
foreach (VariableInitializer v in fixedStatement.Variables) {
if (v.Name == name)
return null; // no need to introduce the variable here
}
}
ForeachStatement foreachStatement = node as ForeachStatement;
if (foreachStatement != null) {
if (foreachStatement.VariableName == name)
return null; // no need to introduce the variable here
}
AstNode withinCurrent = FindInsertPos(node.FirstChild, name, allowPassIntoLoops);
if (withinCurrent != null) {
if (pos == null) {
pos = node;
withinPos = withinCurrent;
} else {
return pos;
}
}
node = node.NextSibling;
}
if (withinPos != null && withinPos.Role == BlockStatement.StatementRole && AllowPassInto(pos, allowPassIntoLoops))
return withinPos;
else
return pos;
}
static bool AllowPassInto(AstNode node, bool allowPassIntoLoops)
{
if (node is AnonymousMethodExpression || node is LambdaExpression)
return false;
return allowPassIntoLoops;
return true;
}
#endregion
}
}

27
ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs

@ -179,23 +179,33 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return true; return true;
} }
static readonly ExpressionStatement displayClassAssignmentPattern =
new ExpressionStatement(new AssignmentExpression(
new NamedNode("variable", new IdentifierExpression()),
new ObjectCreateExpression { Type = new AnyNode("type") }
));
public override object VisitBlockStatement(BlockStatement blockStatement, object data) public override object VisitBlockStatement(BlockStatement blockStatement, object data)
{ {
base.VisitBlockStatement(blockStatement, data); base.VisitBlockStatement(blockStatement, data);
foreach (VariableDeclarationStatement stmt in blockStatement.Statements.OfType<VariableDeclarationStatement>().ToArray()) { foreach (ExpressionStatement stmt in blockStatement.Statements.OfType<ExpressionStatement>().ToArray()) {
if (stmt.Variables.Count() != 1) Match displayClassAssignmentMatch = displayClassAssignmentPattern.Match(stmt);
if (displayClassAssignmentMatch == null)
continue;
ILVariable variable = displayClassAssignmentMatch.Get("variable").Single().Annotation<ILVariable>();
if (variable == null)
continue; continue;
var variable = stmt.Variables.Single(); TypeDefinition type = variable.Type.ResolveWithinSameModule();
TypeDefinition type = stmt.Type.Annotation<TypeReference>().ResolveWithinSameModule();
if (!IsPotentialClosure(context, type)) if (!IsPotentialClosure(context, type))
continue; continue;
ObjectCreateExpression oce = variable.Initializer as ObjectCreateExpression; if (displayClassAssignmentMatch.Get("type").Single().Annotation<TypeReference>().ResolveWithinSameModule() != type)
if (oce == null || oce.Type.Annotation<TypeReference>().ResolveWithinSameModule() != type || oce.Arguments.Any() || !oce.Initializer.IsNull)
continue; continue;
// Looks like we found a display class creation. Now let's verify that the variable is used only for field accesses: // Looks like we found a display class creation. Now let's verify that the variable is used only for field accesses:
bool ok = true; bool ok = true;
foreach (var identExpr in blockStatement.Descendants.OfType<IdentifierExpression>()) { foreach (var identExpr in blockStatement.Descendants.OfType<IdentifierExpression>()) {
if (identExpr.Identifier == variable.Name) { if (identExpr.Identifier == variable.Name && identExpr != displayClassAssignmentMatch.Get("variable").Single()) {
if (!(identExpr.Parent is MemberReferenceExpression && identExpr.Parent.Annotation<FieldReference>() != null)) if (!(identExpr.Parent is MemberReferenceExpression && identExpr.Parent.Annotation<FieldReference>() != null))
ok = false; ok = false;
} }
@ -267,9 +277,10 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
// Now insert the variable declarations (we can do this after the replacements only so that the scope detection works): // Now insert the variable declarations (we can do this after the replacements only so that the scope detection works):
foreach (var tuple in variablesToDeclare) { foreach (var tuple in variablesToDeclare) {
var newVarDecl = DeclareVariableInSmallestScope.DeclareVariable(blockStatement, tuple.Item1, tuple.Item2, allowPassIntoLoops: false); var newVarDecl = new VariableDeclarationStatement(tuple.Item1, tuple.Item2);
if (newVarDecl != null) if (newVarDecl != null)
newVarDecl.Variables.Single().AddAnnotation(new CapturedVariableAnnotation()); newVarDecl.Variables.Single().AddAnnotation(new CapturedVariableAnnotation());
blockStatement.Statements.InsertAfter(null, newVarDecl);
} }
} }
return null; return null;

2
ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs

@ -25,7 +25,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public void Run(AstNode compilationUnit) public void Run(AstNode compilationUnit)
{ {
// Don't show using when decompiling a single method or nested types: // Don't show using when decompiling a single method or nested types:
if (context.CurrentMethod != null || (context.CurrentType != null && context.CurrentType.IsNested)) if (context.CurrentType != null)
return; return;
// First determine all the namespaces that need to be imported: // First determine all the namespaces that need to be imported:

190
ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.PatternMatching; using ICSharpCode.NRefactory.CSharp.PatternMatching;
using Mono.Cecil; using Mono.Cecil;
@ -37,23 +38,23 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return node; return node;
} }
public override AstNode VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, object data) public override AstNode VisitExpressionStatement(ExpressionStatement expressionStatement, object data)
{ {
AstNode result; AstNode result;
if (context.Settings.UsingStatement) { if (context.Settings.UsingStatement) {
result = TransformUsings(variableDeclarationStatement); result = TransformUsings(expressionStatement);
if (result != null) if (result != null)
return result; return result;
} }
result = TransformFor(variableDeclarationStatement); result = TransformFor(expressionStatement);
if (result != null) if (result != null)
return result; return result;
if (context.Settings.LockStatement) { if (context.Settings.LockStatement) {
result = TransformLock(variableDeclarationStatement); result = TransformLock(expressionStatement);
if (result != null) if (result != null)
return result; return result;
} }
return base.VisitVariableDeclarationStatement(variableDeclarationStatement, data); return base.VisitExpressionStatement(expressionStatement, data);
} }
public override AstNode VisitUsingStatement(UsingStatement usingStatement, object data) public override AstNode VisitUsingStatement(UsingStatement usingStatement, object data)
@ -117,27 +118,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
/// <summary> /// <summary>
/// $type $variable = $initializer; /// $type $variable = $initializer;
/// </summary> /// </summary>
static readonly AstNode variableDeclPattern = new VariableDeclarationStatement { static readonly AstNode variableAssignPattern = new ExpressionStatement(
Type = new AnyNode("type"), new AssignmentExpression(
Variables = { new NamedNode("variable", new IdentifierExpression()),
new NamedNode( new AnyNode("initializer")
"variable", ));
new VariableInitializer {
Initializer = new AnyNode("initializer")
}
)
}
};
/// <summary>
/// Variable declaration without initializer.
/// </summary>
static readonly AstNode simpleVariableDefinition = new VariableDeclarationStatement {
Type = new AnyNode(),
Variables = {
new VariableInitializer() // any name but no initializer
}
};
#region using #region using
static readonly AstNode usingTryCatchPattern = new TryCatchStatement { static readonly AstNode usingTryCatchPattern = new TryCatchStatement {
@ -163,20 +148,18 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
}; };
public UsingStatement TransformUsings(VariableDeclarationStatement node) public UsingStatement TransformUsings(ExpressionStatement node)
{ {
Match m1 = variableDeclPattern.Match(node); Match m1 = variableAssignPattern.Match(node);
if (m1 == null) return null; if (m1 == null) return null;
AstNode tryCatch = node.NextSibling; AstNode tryCatch = node.NextSibling;
while (simpleVariableDefinition.Match(tryCatch) != null)
tryCatch = tryCatch.NextSibling;
Match m2 = usingTryCatchPattern.Match(tryCatch); Match m2 = usingTryCatchPattern.Match(tryCatch);
if (m2 == null) return null; if (m2 == null) return null;
if (m1.Get<VariableInitializer>("variable").Single().Name == m2.Get<IdentifierExpression>("ident").Single().Identifier) { if (m1.Get<IdentifierExpression>("variable").Single().Identifier == m2.Get<IdentifierExpression>("ident").Single().Identifier) {
if (m2.Has("valueType")) { if (m2.Has("valueType")) {
// if there's no if(x!=null), then it must be a value type // if there's no if(x!=null), then it must be a value type
TypeReference tr = m1.Get<AstType>("type").Single().Annotation<TypeReference>(); ILVariable v = m1.Get("variable").Single().Annotation<ILVariable>();
if (tr == null || !tr.IsValueType) if (v == null || v.Type == null || !v.Type.IsValueType)
return null; return null;
} }
BlockStatement body = m2.Get<BlockStatement>("body").Single(); BlockStatement body = m2.Get<BlockStatement>("body").Single();
@ -184,6 +167,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
usingStatement.ResourceAcquisition = node.Detach(); usingStatement.ResourceAcquisition = node.Detach();
usingStatement.EmbeddedStatement = body.Detach(); usingStatement.EmbeddedStatement = body.Detach();
tryCatch.ReplaceWith(usingStatement); tryCatch.ReplaceWith(usingStatement);
// TODO: Move the variable declaration into the resource acquisition, if possible
// This is necessary for the foreach-pattern to work on the result of the using-pattern
return usingStatement; return usingStatement;
} }
return null; return null;
@ -203,55 +188,22 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
) )
} }
}, },
EmbeddedStatement = new Choice { EmbeddedStatement = new BlockStatement {
// There are two forms of the foreach statement: new Repeat(
// one where the item variable is declared inside the loop, new NamedNode("variableDeclaration", new VariableDeclarationStatement { Type = new AnyNode(), Variables = { new AnyNode() } })
// and one where it is declared outside of the loop. ).ToStatement(),
// In the former case, we can apply the foreach pattern only if the variable wasn't captured. new WhileStatement {
{ "itemVariableInsideLoop", Condition = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Invoke("MoveNext"),
new BlockStatement { EmbeddedStatement = new BlockStatement {
new WhileStatement { new AssignmentExpression {
Condition = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Invoke("MoveNext"), Left = new NamedNode("itemVariable", new IdentifierExpression()),
EmbeddedStatement = new BlockStatement { Operator = AssignmentOperatorType.Assign,
new VariableDeclarationStatement { Right = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Member("Current")
Type = new AnyNode("itemType"),
Variables = {
new NamedNode(
"itemVariable",
new VariableInitializer {
Initializer = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Member("Current")
}
)
}
},
new Repeat(new AnyNode("statement")).ToStatement()
}
}
}
},
{ "itemVariableOutsideLoop",
new BlockStatement {
new VariableDeclarationStatement {
Type = new AnyNode("itemType"),
Variables = {
new NamedNode("itemVariable", new VariableInitializer())
}
}, },
new WhileStatement { new Repeat(new AnyNode("statement")).ToStatement()
Condition = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Invoke("MoveNext"),
EmbeddedStatement = new BlockStatement {
new AssignmentExpression {
Left = new IdentifierExpressionBackreference("itemVariable"),
Operator = AssignmentOperatorType.Assign,
Right = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Member("Current")
},
new Repeat(new AnyNode("statement")).ToStatement()
}
}
} }
} }
}.ToStatement() }};
};
public ForeachStatement TransformForeach(UsingStatement node) public ForeachStatement TransformForeach(UsingStatement node)
{ {
@ -259,14 +211,18 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
if (m == null) if (m == null)
return null; return null;
VariableInitializer enumeratorVar = m.Get<VariableInitializer>("enumeratorVariable").Single(); VariableInitializer enumeratorVar = m.Get<VariableInitializer>("enumeratorVariable").Single();
VariableInitializer itemVar = m.Get<VariableInitializer>("itemVariable").Single(); ILVariable itemVar = m.Get("itemVariable").Single().Annotation<ILVariable>();
if (m.Has("itemVariableInsideLoop") && itemVar.Annotation<DelegateConstruction.CapturedVariableAnnotation>() != null) { if (itemVar == null)
// cannot move captured variables out of loops
return null; return null;
} return null; // TODO
// if (m.Has("itemVariableInsideLoop") && itemVar.Annotation<DelegateConstruction.CapturedVariableAnnotation>() != null) {
// // cannot move captured variables out of loops
// return null;
// }
BlockStatement newBody = new BlockStatement(); BlockStatement newBody = new BlockStatement();
foreach (Statement stmt in m.Get<Statement>("statement")) foreach (Statement stmt in m.Get<Statement>("statement"))
newBody.Add(stmt.Detach()); newBody.Add(stmt.Detach());
ForeachStatement foreachStatement = new ForeachStatement { ForeachStatement foreachStatement = new ForeachStatement {
VariableType = m.Get<AstType>("itemType").Single().Detach(), VariableType = m.Get<AstType>("itemType").Single().Detach(),
VariableName = itemVar.Name, VariableName = itemVar.Name,
@ -299,17 +255,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
}}; }};
public ForStatement TransformFor(VariableDeclarationStatement node) public ForStatement TransformFor(ExpressionStatement node)
{ {
Match m1 = variableDeclPattern.Match(node); Match m1 = variableAssignPattern.Match(node);
if (m1 == null) return null; if (m1 == null) return null;
AstNode next = node.NextSibling; AstNode next = node.NextSibling;
while (simpleVariableDefinition.Match(next) != null)
next = next.NextSibling;
Match m2 = forPattern.Match(next); Match m2 = forPattern.Match(next);
if (m2 == null) return null; if (m2 == null) return null;
// ensure the variable in the for pattern is the same as in the declaration // ensure the variable in the for pattern is the same as in the declaration
if (m1.Get<VariableInitializer>("variable").Single().Name != m2.Get<IdentifierExpression>("ident").Single().Identifier) if (m1.Get<IdentifierExpression>("variable").Single().Identifier != m2.Get<IdentifierExpression>("ident").Single().Identifier)
return null; return null;
WhileStatement loop = (WhileStatement)next; WhileStatement loop = (WhileStatement)next;
node.Remove(); node.Remove();
@ -374,16 +328,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
#endregion #endregion
#region lock #region lock
static readonly AstNode lockFlagInitPattern = new VariableDeclarationStatement { static readonly AstNode lockFlagInitPattern = new ExpressionStatement(
Type = new PrimitiveType("bool"), new AssignmentExpression(
Variables = { new NamedNode("variable", new IdentifierExpression()),
new NamedNode( new PrimitiveExpression(false)
"variable", ));
new VariableInitializer {
Initializer = new PrimitiveExpression(false)
}
)
}};
static readonly AstNode lockTryCatchPattern = new TryCatchStatement { static readonly AstNode lockTryCatchPattern = new TryCatchStatement {
TryBlock = new BlockStatement { TryBlock = new BlockStatement {
@ -404,16 +353,14 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
}}; }};
public LockStatement TransformLock(VariableDeclarationStatement node) public LockStatement TransformLock(ExpressionStatement node)
{ {
Match m1 = lockFlagInitPattern.Match(node); Match m1 = lockFlagInitPattern.Match(node);
if (m1 == null) return null; if (m1 == null) return null;
AstNode tryCatch = node.NextSibling; AstNode tryCatch = node.NextSibling;
while (simpleVariableDefinition.Match(tryCatch) != null)
tryCatch = tryCatch.NextSibling;
Match m2 = lockTryCatchPattern.Match(tryCatch); Match m2 = lockTryCatchPattern.Match(tryCatch);
if (m2 == null) return null; if (m2 == null) return null;
if (m1.Get<VariableInitializer>("variable").Single().Name == m2.Get<IdentifierExpression>("flag").Single().Identifier) { if (m1.Get<IdentifierExpression>("variable").Single().Identifier == m2.Get<IdentifierExpression>("flag").Single().Identifier) {
Expression enter = m2.Get<Expression>("enter").Single(); Expression enter = m2.Get<Expression>("enter").Single();
IdentifierExpression exit = m2.Get<IdentifierExpression>("exit").Single(); IdentifierExpression exit = m2.Get<IdentifierExpression>("exit").Single();
if (exit.Match(enter) == null) { if (exit.Match(enter) == null) {
@ -630,32 +577,25 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
#region Automatic Events #region Automatic Events
static readonly Accessor automaticEventPatternV4 = new Accessor { static readonly Accessor automaticEventPatternV4 = new Accessor {
Body = new BlockStatement { Body = new BlockStatement {
new VariableDeclarationStatement { new VariableDeclarationStatement { Type = new AnyNode("type"), Variables = { new AnyNode() } },
Type = new AnyNode("type"), new VariableDeclarationStatement { Type = new Backreference("type"), Variables = { new AnyNode() } },
Variables = { new VariableDeclarationStatement { Type = new Backreference("type"), Variables = { new AnyNode() } },
new NamedNode( new AssignmentExpression {
"var1", new VariableInitializer { Left = new NamedNode("var1", new IdentifierExpression()),
Initializer = new NamedNode("field", new MemberReferenceExpression { Target = new ThisReferenceExpression() }) Operator = AssignmentOperatorType.Assign,
})} Right = new NamedNode("field", new MemberReferenceExpression { Target = new ThisReferenceExpression() })
},
new VariableDeclarationStatement {
Type = new Backreference("type"),
Variables = { new NamedNode("var2", new VariableInitializer()) }
}, },
new DoWhileStatement { new DoWhileStatement {
EmbeddedStatement = new BlockStatement { EmbeddedStatement = new BlockStatement {
new AssignmentExpression(new IdentifierExpressionBackreference("var2"), new IdentifierExpressionBackreference("var1")), new AssignmentExpression(new NamedNode("var2", new IdentifierExpression()), new IdentifierExpressionBackreference("var1")),
new VariableDeclarationStatement { new AssignmentExpression {
Type = new Backreference("type"), Left = new NamedNode("var3", new IdentifierExpression()),
Variables = { Operator = AssignmentOperatorType.Assign,
new NamedNode( Right = new AnyNode("delegateCombine").ToExpression().Invoke(
"var3", new VariableInitializer { new IdentifierExpressionBackreference("var2"),
Initializer = new AnyNode("delegateCombine").ToExpression().Invoke( new IdentifierExpression("value")
new IdentifierExpressionBackreference("var2"), ).CastTo(new Backreference("type"))
new IdentifierExpression("value") },
).CastTo(new Backreference("type"))
})
}},
new AssignmentExpression { new AssignmentExpression {
Left = new IdentifierExpressionBackreference("var1"), Left = new IdentifierExpressionBackreference("var1"),
Right = new TypePattern(typeof(System.Threading.Interlocked)).ToType().Invoke( Right = new TypePattern(typeof(System.Threading.Interlocked)).ToType().Invoke(

1
ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs

@ -23,6 +23,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
new ConvertConstructorCallIntoInitializer(), new ConvertConstructorCallIntoInitializer(),
new ReplaceMethodCallsWithOperators(), new ReplaceMethodCallsWithOperators(),
new IntroduceUnsafeModifier(), new IntroduceUnsafeModifier(),
new DeclareVariables(context),
new IntroduceUsingDeclarations(context) new IntroduceUsingDeclarations(context)
}; };
} }

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -53,13 +53,13 @@
<Compile Include="Ast\AstMethodBodyBuilder.cs" /> <Compile Include="Ast\AstMethodBodyBuilder.cs" />
<Compile Include="Ast\CecilTypeResolveContext.cs" /> <Compile Include="Ast\CecilTypeResolveContext.cs" />
<Compile Include="Ast\CommentStatement.cs" /> <Compile Include="Ast\CommentStatement.cs" />
<Compile Include="Ast\DeclareVariableInSmallestScope.cs" />
<Compile Include="Ast\DecompilerContext.cs" /> <Compile Include="Ast\DecompilerContext.cs" />
<Compile Include="Ast\NameVariables.cs" /> <Compile Include="Ast\NameVariables.cs" />
<Compile Include="Ast\NRefactoryExtensions.cs" /> <Compile Include="Ast\NRefactoryExtensions.cs" />
<Compile Include="Ast\TextOutputFormatter.cs" /> <Compile Include="Ast\TextOutputFormatter.cs" />
<Compile Include="Ast\Transforms\ContextTrackingVisitor.cs" /> <Compile Include="Ast\Transforms\ContextTrackingVisitor.cs" />
<Compile Include="Ast\Transforms\ConvertConstructorCallIntoInitializer.cs" /> <Compile Include="Ast\Transforms\ConvertConstructorCallIntoInitializer.cs" />
<Compile Include="Ast\Transforms\DeclareVariables.cs" />
<Compile Include="Ast\Transforms\DelegateConstruction.cs" /> <Compile Include="Ast\Transforms\DelegateConstruction.cs" />
<Compile Include="Ast\Transforms\IntroduceUnsafeModifier.cs" /> <Compile Include="Ast\Transforms\IntroduceUnsafeModifier.cs" />
<Compile Include="Ast\Transforms\IntroduceUsingDeclarations.cs" /> <Compile Include="Ast\Transforms\IntroduceUsingDeclarations.cs" />

Loading…
Cancel
Save