mirror of https://github.com/icsharpcode/ILSpy.git
13 changed files with 258 additions and 471 deletions
@ -1,56 +0,0 @@
@@ -1,56 +0,0 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using ICSharpCode.NRefactory.CSharp; |
||||
|
||||
namespace Decompiler.Transforms.Ast |
||||
{ |
||||
public class RemoveDeadLabels : DepthFirstAstVisitor<object, object> |
||||
{ |
||||
List<string> usedLabels = new List<string>(); |
||||
bool collectingUsedLabels; |
||||
|
||||
public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) |
||||
{ |
||||
collectingUsedLabels = true; |
||||
base.VisitConstructorDeclaration(constructorDeclaration, data); |
||||
collectingUsedLabels = false; |
||||
base.VisitConstructorDeclaration(constructorDeclaration, data); |
||||
return null; |
||||
} |
||||
|
||||
public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) |
||||
{ |
||||
collectingUsedLabels = true; |
||||
base.VisitMethodDeclaration(methodDeclaration, data); |
||||
collectingUsedLabels = false; |
||||
base.VisitMethodDeclaration(methodDeclaration, data); |
||||
return null; |
||||
} |
||||
|
||||
public override object VisitAccessor(Accessor accessor, object data) |
||||
{ |
||||
collectingUsedLabels = true; |
||||
base.VisitAccessor(accessor, data); |
||||
collectingUsedLabels = false; |
||||
return base.VisitAccessor(accessor, data); |
||||
} |
||||
|
||||
public override object VisitGotoStatement(GotoStatement gotoStatement, object data) |
||||
{ |
||||
if (collectingUsedLabels) { |
||||
usedLabels.Add(gotoStatement.Label); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public override object VisitLabelStatement(LabelStatement labelStatement, object data) |
||||
{ |
||||
if (!collectingUsedLabels) { |
||||
if (!usedLabels.Contains(labelStatement.Label)) { |
||||
labelStatement.Remove(); |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
} |
||||
} |
@ -1,19 +0,0 @@
@@ -1,19 +0,0 @@
|
||||
using System; |
||||
using System.Linq; |
||||
using ICSharpCode.NRefactory.CSharp; |
||||
|
||||
namespace Decompiler.Transforms.Ast |
||||
{ |
||||
public class RemoveEmptyElseBody: DepthFirstAstVisitor<object, object> |
||||
{ |
||||
public override object VisitIfElseStatement(IfElseStatement ifElseStatement, object data) |
||||
{ |
||||
base.VisitIfElseStatement(ifElseStatement, data); |
||||
BlockStatement block = ifElseStatement.FalseStatement as BlockStatement; |
||||
if (block != null && !block.Statements.Any()) { |
||||
ifElseStatement.FalseStatement = null; |
||||
} |
||||
return null; |
||||
} |
||||
} |
||||
} |
@ -1,207 +0,0 @@
@@ -1,207 +0,0 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using ICSharpCode.NRefactory.CSharp; |
||||
|
||||
namespace Decompiler.Transforms.Ast |
||||
{ |
||||
public class RemoveGotos: DepthFirstAstVisitor<object, object> |
||||
{ |
||||
Stack<Statement> enteredLoops = new Stack<Statement>(); |
||||
|
||||
Statement CurrentLoop { |
||||
get { |
||||
if (enteredLoops.Count > 0) { |
||||
return enteredLoops.Peek(); |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
|
||||
Statement CurrentLoopBody { |
||||
get { |
||||
if (this.CurrentLoop == null) { |
||||
return null; |
||||
} else if (this.CurrentLoop is ForStatement) { |
||||
return ((ForStatement)this.CurrentLoop).EmbeddedStatement; |
||||
} else if (this.CurrentLoop is WhileStatement) { |
||||
return ((WhileStatement)this.CurrentLoop).EmbeddedStatement; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public override object VisitForStatement(ForStatement forStatement, object data) |
||||
{ |
||||
enteredLoops.Push(forStatement); |
||||
base.VisitForStatement(forStatement, data); |
||||
enteredLoops.Pop(); |
||||
return null; |
||||
} |
||||
|
||||
public override object VisitWhileStatement(WhileStatement whileStatement, object data) |
||||
{ |
||||
enteredLoops.Push(whileStatement); |
||||
base.VisitWhileStatement(whileStatement, data); |
||||
enteredLoops.Pop(); |
||||
return null; |
||||
} |
||||
|
||||
public override object VisitBlockStatement(BlockStatement blockStatement, object data) |
||||
{ |
||||
base.VisitBlockStatement(blockStatement, data); |
||||
|
||||
// Remove redundant jump at the end of block
|
||||
AstNode lastStmt = blockStatement.Children.LastOrDefault(); |
||||
// End of for loop
|
||||
if (lastStmt is ContinueStatement && |
||||
blockStatement.Parent is ForStatement) |
||||
{ |
||||
lastStmt.Remove(); |
||||
return null; |
||||
} |
||||
// End of method
|
||||
if (lastStmt is ReturnStatement && |
||||
(blockStatement.Parent is MethodDeclaration || blockStatement.Parent is ConstructorDeclaration) && |
||||
((ReturnStatement)lastStmt).Expression.IsNull) |
||||
{ |
||||
lastStmt.Remove(); |
||||
return null; |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
// Get the next statement that will be executed after this one
|
||||
// May return null
|
||||
public static AstNode GetNextStatement(Statement statement) |
||||
{ |
||||
if (statement == null) throw new ArgumentNullException(); |
||||
|
||||
Statement next = (Statement)statement.NextSibling; |
||||
|
||||
if (next != null) { |
||||
return EnterBlockStatement(next); |
||||
} else { |
||||
if (statement.Parent is BlockStatement && |
||||
statement.Parent.Parent is Statement) { |
||||
return ExitBlockStatement((Statement)statement.Parent.Parent); |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Get the statement that will be executed once the given block exits by the end brace
|
||||
// May return null
|
||||
public static AstNode ExitBlockStatement(Statement statement) |
||||
{ |
||||
if (statement == null) throw new ArgumentNullException(); |
||||
|
||||
// When an 'if' body is finished the execution continues with the
|
||||
// next statement after the 'if' statement
|
||||
if (statement is IfElseStatement) { |
||||
return GetNextStatement((IfElseStatement)statement); |
||||
} |
||||
|
||||
// When a 'for' body is finished the execution continues by:
|
||||
// Iterator; Condition; Body
|
||||
if (statement is ForStatement) { |
||||
ForStatement forLoop = statement as ForStatement; |
||||
if (forLoop.Iterators.Any()) { |
||||
return forLoop.Iterators.First(); |
||||
} else if (!forLoop.Condition.IsNull) { |
||||
return forLoop.Condition; |
||||
} else { |
||||
return EnterBlockStatement((Statement)forLoop.EmbeddedStatement.FirstChild); |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
// Get the first statement that will be executed in the given block
|
||||
public static AstNode EnterBlockStatement(Statement statement) |
||||
{ |
||||
if (statement == null) throw new ArgumentNullException(); |
||||
|
||||
// For loop starts as follows: Initializers; Condition; Body
|
||||
if (statement is ForStatement) { |
||||
ForStatement forLoop = statement as ForStatement; |
||||
if (forLoop.Initializers.Any()) { |
||||
return forLoop.Initializers.First(); |
||||
} else if (!forLoop.Condition.IsNull) { |
||||
return forLoop.Condition; |
||||
} else if (forLoop.EmbeddedStatement.Children.FirstOrDefault() is Statement) { |
||||
return EnterBlockStatement((Statement)forLoop.EmbeddedStatement.FirstChild); // Simplify again
|
||||
} |
||||
} |
||||
|
||||
return statement; // Can not simplify
|
||||
} |
||||
|
||||
public override object VisitGotoStatement(GotoStatement gotoStatement, object data) |
||||
{ |
||||
// Remove redundant goto which goes to a label that imideately follows
|
||||
AstNode fallthoughTarget = GetNextStatement(gotoStatement); |
||||
while(true) { |
||||
if (fallthoughTarget is LabelStatement) { |
||||
if ((fallthoughTarget as LabelStatement).Label == gotoStatement.Label) { |
||||
gotoStatement.Remove(); |
||||
return null; |
||||
} else { |
||||
fallthoughTarget = GetNextStatement((LabelStatement)fallthoughTarget); |
||||
continue; |
||||
} |
||||
} |
||||
break; |
||||
} |
||||
|
||||
// Replace goto with 'break'
|
||||
// Break statement moves right outside the looop
|
||||
if (CurrentLoop != null) { |
||||
AstNode breakTarget = GetNextStatement(CurrentLoop); |
||||
if ((breakTarget is LabelStatement) && |
||||
(breakTarget as LabelStatement).Label == gotoStatement.Label) { |
||||
gotoStatement.ReplaceWith(new BreakStatement()); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
// Replace goto with 'continue'
|
||||
// Continue statement which moves at the very end of loop
|
||||
if (CurrentLoop != null && |
||||
(CurrentLoopBody is BlockStatement) && |
||||
((CurrentLoopBody as BlockStatement).LastChild as LabelStatement) != null && |
||||
((CurrentLoopBody as BlockStatement).LastChild as LabelStatement).Label == gotoStatement.Label) { |
||||
gotoStatement.ReplaceWith(new ContinueStatement()); |
||||
return null; |
||||
} |
||||
|
||||
// Replace goto with 'continue'
|
||||
// Jump before while
|
||||
if (CurrentLoop is WhileStatement && |
||||
CurrentLoop.PrevSibling != null && |
||||
CurrentLoop.PrevSibling is LabelStatement && |
||||
(CurrentLoop.PrevSibling as LabelStatement).Label == gotoStatement.Label) { |
||||
gotoStatement.ReplaceWith(new ContinueStatement()); |
||||
return null; |
||||
} |
||||
|
||||
// Replace goto with 'continue'
|
||||
// Continue statement which moves at the very start of for loop
|
||||
if (CurrentLoop != null) { |
||||
AstNode continueTarget = ExitBlockStatement(CurrentLoop); // The start of the loop
|
||||
if ((continueTarget is LabelStatement) && |
||||
(continueTarget as LabelStatement).Label == gotoStatement.Label) { |
||||
gotoStatement.ReplaceWith(new ContinueStatement()); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
} |
||||
} |
@ -1,73 +0,0 @@
@@ -1,73 +0,0 @@
|
||||
using System; |
||||
using System.Linq; |
||||
using ICSharpCode.NRefactory.CSharp; |
||||
|
||||
namespace Decompiler.Transforms.Ast |
||||
{ |
||||
public class RestoreLoop: DepthFirstAstVisitor<object, object> |
||||
{ |
||||
public override object VisitForStatement(ForStatement forStatement, object data) |
||||
{ |
||||
base.VisitForStatement(forStatement, data); |
||||
|
||||
// Restore loop initializer
|
||||
if (!forStatement.Initializers.Any()) { |
||||
VariableDeclarationStatement varDeclr = forStatement.PrevSibling as VariableDeclarationStatement; |
||||
if (varDeclr != null) { |
||||
varDeclr.ReplaceWith(Statement.Null); |
||||
forStatement.Initializers.Add(varDeclr); |
||||
} |
||||
} |
||||
|
||||
// Restore loop condition
|
||||
if (forStatement.Condition.IsNull && |
||||
forStatement.EmbeddedStatement.Children.Count() >= 3) |
||||
{ |
||||
IfElseStatement condition = forStatement.EmbeddedStatement.Children.First() as IfElseStatement; |
||||
BreakStatement breakStmt = forStatement.EmbeddedStatement.Children.Skip(1).First() as BreakStatement; |
||||
LabelStatement label = forStatement.EmbeddedStatement.Children.Skip(2).First() as LabelStatement; |
||||
if (condition != null && breakStmt != null && label != null && |
||||
condition.TrueStatement.Children.Count() == 1) |
||||
{ |
||||
GotoStatement gotoStmt = condition.TrueStatement.FirstChild as GotoStatement; |
||||
if (gotoStmt != null && gotoStmt.Label == label.Label) { |
||||
condition.Remove(); |
||||
breakStmt.Remove(); |
||||
forStatement.Condition = condition.Condition; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Restore loop condition (version 2)
|
||||
if (forStatement.Condition.IsNull) { |
||||
IfElseStatement condition = forStatement.EmbeddedStatement.FirstChild as IfElseStatement; |
||||
if (condition != null && |
||||
condition.TrueStatement.Children.Any() && |
||||
condition.TrueStatement.FirstChild is BlockStatement && |
||||
condition.TrueStatement.Children.Count() == 1 && |
||||
condition.TrueStatement.FirstChild.FirstChild is BreakStatement && |
||||
condition.FalseStatement.Children.Any() && |
||||
condition.FalseStatement.FirstChild is BlockStatement && |
||||
condition.FalseStatement.Children.Count() == 0) |
||||
{ |
||||
condition.Remove(); |
||||
forStatement.Condition = new UnaryOperatorExpression() { Expression = condition.Condition, Operator = UnaryOperatorType.Not }; |
||||
} |
||||
} |
||||
|
||||
// Restore loop iterator
|
||||
if (forStatement.EmbeddedStatement.Children.Any() && |
||||
!forStatement.Iterators.Any()) |
||||
{ |
||||
ExpressionStatement lastStmt = forStatement.EmbeddedStatement.LastChild as ExpressionStatement; |
||||
if (lastStmt != null && |
||||
(lastStmt.Expression is AssignmentExpression || lastStmt.Expression is UnaryOperatorExpression)) { |
||||
lastStmt.Remove(); |
||||
forStatement.Iterators.Add(lastStmt); |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,202 @@
@@ -0,0 +1,202 @@
|
||||
using System; |
||||
using System.Diagnostics; |
||||
using System.IO; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using Decompiler.ControlFlow; |
||||
|
||||
namespace Decompiler |
||||
{ |
||||
public class GotoRemoval |
||||
{ |
||||
Dictionary<ILNode, ILNode> parent = new Dictionary<ILNode, ILNode>(); |
||||
Dictionary<ILNode, ILNode> nextSibling = new Dictionary<ILNode, ILNode>(); |
||||
|
||||
public bool RemoveGotos(ILBlock method) |
||||
{ |
||||
// Build the navigation data
|
||||
parent[method] = null; |
||||
foreach (ILNode node in method.GetSelfAndChildrenRecursive<ILNode>()) { |
||||
ILNode previousChild = null; |
||||
foreach (ILNode child in node.GetChildren()) { |
||||
Debug.Assert(!parent.ContainsKey(child)); |
||||
parent[child] = node; |
||||
if (previousChild != null) |
||||
nextSibling[previousChild] = child; |
||||
previousChild = child; |
||||
} |
||||
if (previousChild != null) |
||||
nextSibling[previousChild] = null; |
||||
} |
||||
|
||||
// Simplify gotos
|
||||
bool modified = false; |
||||
foreach (ILExpression gotoExpr in method.GetSelfAndChildrenRecursive<ILExpression>().Where(e => e.Code == ILCode.Br || e.Code == ILCode.Leave)) { |
||||
modified |= TrySimplifyGoto(gotoExpr); |
||||
} |
||||
|
||||
// Remove dead lables and nops
|
||||
HashSet<ILLabel> liveLabels = new HashSet<ILLabel>(method.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.GetBranchTargets())); |
||||
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) { |
||||
int oldBodyCount = block.Body.Count; |
||||
block.Body = block.Body.Where(n => !n.Matches(ILCode.Nop) && !(n is ILLabel && !liveLabels.Contains((ILLabel)n))).ToList(); |
||||
modified |= block.Body.Count < oldBodyCount; |
||||
} |
||||
|
||||
// Remove redundant continue
|
||||
foreach(ILWhileLoop loop in method.GetSelfAndChildrenRecursive<ILWhileLoop>()) { |
||||
var body = loop.BodyBlock.Body; |
||||
if (body.Count > 0 && body.Last().Matches(ILCode.LoopContinue)) { |
||||
body.RemoveAt(body.Count - 1); |
||||
} |
||||
} |
||||
|
||||
// Remove redundant return
|
||||
if (method.Body.Count > 0 && method.Body.Last().Matches(ILCode.Ret) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) { |
||||
method.Body.RemoveAt(method.Body.Count - 1); |
||||
} |
||||
|
||||
return modified; |
||||
} |
||||
|
||||
bool TrySimplifyGoto(ILExpression gotoExpr) |
||||
{ |
||||
Debug.Assert(gotoExpr.Code == ILCode.Br || gotoExpr.Code == ILCode.Leave); |
||||
Debug.Assert(gotoExpr.Prefixes == null); |
||||
Debug.Assert(gotoExpr.Operand != null); |
||||
|
||||
ILExpression target = Enter(gotoExpr, new HashSet<ILNode>()); |
||||
if (target == null) |
||||
return false; |
||||
|
||||
if (target == Exit(gotoExpr, new HashSet<ILNode>())) { |
||||
gotoExpr.Code = ILCode.Nop; |
||||
gotoExpr.Operand = null; |
||||
target.ILRanges.AddRange(gotoExpr.ILRanges); |
||||
gotoExpr.ILRanges.Clear(); |
||||
return true; |
||||
} |
||||
|
||||
ILWhileLoop loop = null; |
||||
ILNode current = gotoExpr; |
||||
while(loop == null && current != null) { |
||||
current = parent[current]; |
||||
loop = current as ILWhileLoop; |
||||
} |
||||
|
||||
if (loop != null && target == Exit(loop, new HashSet<ILNode>())) { |
||||
gotoExpr.Code = ILCode.LoopBreak; |
||||
gotoExpr.Operand = null; |
||||
return true; |
||||
} |
||||
|
||||
if (loop != null && target == Enter(loop, new HashSet<ILNode>())) { |
||||
gotoExpr.Code = ILCode.LoopContinue; |
||||
gotoExpr.Operand = null; |
||||
return true; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Get the first expression to be excecuted if the instruction pointer is at the start of the given node
|
||||
/// </summary>
|
||||
ILExpression Enter(ILNode node, HashSet<ILNode> visitedNodes) |
||||
{ |
||||
if (node == null) |
||||
throw new ArgumentNullException(); |
||||
|
||||
if (!visitedNodes.Add(node)) |
||||
return null; // Infinite loop
|
||||
|
||||
ILLabel label = node as ILLabel; |
||||
if (label != null) { |
||||
return Exit(label, visitedNodes); |
||||
} |
||||
|
||||
ILExpression expr = node as ILExpression; |
||||
if (expr != null) { |
||||
if (expr.Code == ILCode.Br || expr.Code == ILCode.Leave) { |
||||
return Enter((ILLabel)expr.Operand, visitedNodes); |
||||
} else if (expr.Code == ILCode.Nop) { |
||||
return Exit(expr, visitedNodes); |
||||
} else { |
||||
return expr; |
||||
} |
||||
} |
||||
|
||||
ILBlock block = node as ILBlock; |
||||
if (block != null) { |
||||
if (block.EntryGoto != null) { |
||||
return Enter(block.EntryGoto, visitedNodes); |
||||
} else if (block.Body.Count > 0) { |
||||
return Enter(block.Body[0], visitedNodes); |
||||
} else { |
||||
return Exit(block, visitedNodes); |
||||
} |
||||
} |
||||
|
||||
ILCondition cond = node as ILCondition; |
||||
if (cond != null) { |
||||
return cond.Condition; |
||||
} |
||||
|
||||
ILWhileLoop loop = node as ILWhileLoop; |
||||
if (loop != null) { |
||||
if (loop.Condition != null) { |
||||
return loop.Condition; |
||||
} else { |
||||
return Enter(loop.BodyBlock, visitedNodes); |
||||
} |
||||
} |
||||
|
||||
ILTryCatchBlock tryCatch = node as ILTryCatchBlock; |
||||
if (tryCatch != null) { |
||||
return Enter(tryCatch.TryBlock, visitedNodes); |
||||
} |
||||
|
||||
ILSwitch ilSwitch = node as ILSwitch; |
||||
if (ilSwitch != null) { |
||||
return ilSwitch.Condition; |
||||
} |
||||
|
||||
throw new NotSupportedException(node.GetType().ToString()); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Get the first expression to be excecuted if the instruction pointer is at the end of the given node
|
||||
/// </summary>
|
||||
ILExpression Exit(ILNode node, HashSet<ILNode> visitedNodes) |
||||
{ |
||||
if (node == null) |
||||
throw new ArgumentNullException(); |
||||
|
||||
ILNode nodeParent = parent[node]; |
||||
if (nodeParent == null) |
||||
return null; // Exited main body
|
||||
|
||||
if (nodeParent is ILBlock) { |
||||
ILNode nextNode = nextSibling[node]; |
||||
if (nextNode != null) { |
||||
return Enter(nextNode, visitedNodes); |
||||
} else { |
||||
return Exit(nodeParent, visitedNodes); |
||||
} |
||||
} |
||||
|
||||
if (nodeParent is ILCondition || |
||||
nodeParent is ILTryCatchBlock || |
||||
nodeParent is ILSwitch) |
||||
{ |
||||
return Exit(nodeParent, visitedNodes); |
||||
} |
||||
|
||||
if (nodeParent is ILWhileLoop) { |
||||
return Enter(nodeParent, visitedNodes); |
||||
} |
||||
|
||||
throw new NotSupportedException(nodeParent.GetType().ToString()); |
||||
} |
||||
} |
||||
} |
@ -1,14 +0,0 @@
@@ -1,14 +0,0 @@
|
||||
using System; |
||||
|
||||
namespace Decompiler |
||||
{ |
||||
public static class Options |
||||
{ |
||||
public static readonly bool NodeComments = false; |
||||
public static readonly bool ReduceLoops = true; |
||||
public static readonly bool ReduceConditonals = true; |
||||
public static readonly bool ReduceAstJumps = true; |
||||
public static readonly bool ReduceAstLoops = true; |
||||
public static readonly bool ReduceAstOther = true; |
||||
} |
||||
} |
Loading…
Reference in new issue