Browse Source

Merge branch 'master' of git://github.com/icsharpcode/ILSpy into Debugger

pull/191/merge
Eusebiu Marcu 15 years ago
parent
commit
3bc8525c4b
  1. 4
      Debugger/ILSpy.Debugger/AvalonEdit/TextMarkerService.cs
  2. 2
      Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs
  3. 3
      Debugger/ILSpy.Debugger/Bookmarks/MarkerBookmark.cs
  4. 13
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  5. 3
      ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  6. 56
      ICSharpCode.Decompiler/Ast/Transforms/RemoveDeadLabels.cs
  7. 19
      ICSharpCode.Decompiler/Ast/Transforms/RemoveEmptyElseBody.cs
  8. 207
      ICSharpCode.Decompiler/Ast/Transforms/RemoveGotos.cs
  9. 73
      ICSharpCode.Decompiler/Ast/Transforms/RestoreLoop.cs
  10. 13
      ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs
  11. 6
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  12. 202
      ICSharpCode.Decompiler/ILAst/GotoRemoval.cs
  13. 168
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  14. 26
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  15. 2
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  16. 14
      ICSharpCode.Decompiler/Options.cs

4
Debugger/ILSpy.Debugger/AvalonEdit/TextMarkerService.cs

@ -126,7 +126,7 @@ namespace ILSpy.Debugger.AvalonEdit @@ -126,7 +126,7 @@ namespace ILSpy.Debugger.AvalonEdit
return;
int lineStart = line.Offset;
int lineEnd = lineStart + line.Length;
foreach (TextMarker marker in markers.FindOverlappingSegments(lineStart, line.Length)) {
foreach (TextMarker marker in markers.FindOverlappingSegments(lineStart, line.Length).Reverse()) {
Brush foregroundBrush = null;
if (marker.ForegroundColor != null) {
foregroundBrush = new SolidColorBrush(marker.ForegroundColor.Value);
@ -166,7 +166,7 @@ namespace ILSpy.Debugger.AvalonEdit @@ -166,7 +166,7 @@ namespace ILSpy.Debugger.AvalonEdit
return;
int viewStart = visualLines.First().FirstDocumentLine.Offset;
int viewEnd = visualLines.Last().LastDocumentLine.Offset + visualLines.Last().LastDocumentLine.Length;
foreach (TextMarker marker in markers.FindOverlappingSegments(viewStart, viewEnd - viewStart)) {
foreach (TextMarker marker in markers.FindOverlappingSegments(viewStart, viewEnd - viewStart).Reverse()) {
if (marker.BackgroundColor != null) {
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
geoBuilder.AlignToWholePixels = true;

2
Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs

@ -92,7 +92,7 @@ namespace ILSpy.Debugger.Bookmarks @@ -92,7 +92,7 @@ namespace ILSpy.Debugger.Bookmarks
public override ITextMarker CreateMarker(ITextMarkerService markerService, int offset, int length)
{
ITextMarker marker = markerService.Create(offset + startColumn - 1, Math.Max(endColumn - startColumn, 1));
ITextMarker marker = markerService.Create(offset + startColumn - 1, length);
marker.BackgroundColor = Colors.Yellow;
marker.ForegroundColor = Colors.Blue;
return marker;

3
Debugger/ILSpy.Debugger/Bookmarks/MarkerBookmark.cs

@ -22,9 +22,6 @@ using ILSpy.Debugger.AvalonEdit; @@ -22,9 +22,6 @@ using ILSpy.Debugger.AvalonEdit;
namespace ILSpy.Debugger.Bookmarks
{
/// <summary>
/// Description of MarkerBookmark.
/// </summary>
public abstract class MarkerBookmark : BookmarkBase
{
public MarkerBookmark(string typeName, AstLocation location) : base(typeName, location)

13
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -110,21 +110,18 @@ namespace Decompiler @@ -110,21 +110,18 @@ namespace Decompiler
}
} else if (node is ILWhileLoop) {
ILWhileLoop ilLoop = (ILWhileLoop)node;
if (ilLoop.PreLoopLabel != null)
yield return TransformNode(ilLoop.PreLoopLabel).Single();
WhileStatement whileStmt = new WhileStatement() {
Condition = ilLoop.Condition != null ? MakeBranchCondition(ilLoop.Condition) : new PrimitiveExpression(true),
EmbeddedStatement = TransformBlock(ilLoop.BodyBlock)
};
yield return whileStmt;
if (ilLoop.PostLoopGoto != null)
yield return (Statement)TransformExpression(ilLoop.PostLoopGoto);
} else if (node is ILCondition) {
ILCondition conditionalNode = (ILCondition)node;
bool hasFalseBlock = conditionalNode.FalseBlock.EntryGoto != null || conditionalNode.FalseBlock.Body.Count > 0;
yield return new Ast.IfElseStatement {
Condition = MakeBranchCondition(conditionalNode.Condition),
TrueStatement = TransformBlock(conditionalNode.TrueBlock),
FalseStatement = TransformBlock(conditionalNode.FalseBlock)
FalseStatement = hasFalseBlock ? TransformBlock(conditionalNode.FalseBlock) : null
};
} else if (node is ILSwitch) {
ILSwitch ilSwitch = (ILSwitch)node;
@ -296,6 +293,10 @@ namespace Decompiler @@ -296,6 +293,10 @@ namespace Decompiler
TrueExpression = (Expression)TransformExpression(byteCode.Arguments[1]),
FalseExpression = (Expression)TransformExpression(byteCode.Arguments[2]),
};
case ILCode.LoopBreak:
return new Ast.BreakStatement();
case ILCode.LoopContinue:
return new Ast.ContinueStatement();
}
List<Ast.Expression> args = TransformExpressionArguments(byteCode);
@ -573,7 +574,7 @@ namespace Decompiler @@ -573,7 +574,7 @@ namespace Decompiler
} else {
return InlineAssembly(byteCode, args);
}
case Code.Leave: return null;
case Code.Leave: return new GotoStatement() { Label = ((ILLabel)operand).Name };
case Code.Localloc: return InlineAssembly(byteCode, args);
case Code.Mkrefany: return InlineAssembly(byteCode, args);
case Code.Newobj:

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

@ -204,8 +204,7 @@ namespace Decompiler.Transforms @@ -204,8 +204,7 @@ namespace Decompiler.Transforms
Left = new Backreference("ident").ToExpression(),
Operator = AssignmentOperatorType.Any,
Right = new AnyNode().ToExpression()
})).ToStatement(),
new ContinueStatement()
})).ToStatement()
}
};

56
ICSharpCode.Decompiler/Ast/Transforms/RemoveDeadLabels.cs

@ -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;
}
}
}

19
ICSharpCode.Decompiler/Ast/Transforms/RemoveEmptyElseBody.cs

@ -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;
}
}
}

207
ICSharpCode.Decompiler/Ast/Transforms/RemoveGotos.cs

@ -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;
}
}
}

73
ICSharpCode.Decompiler/Ast/Transforms/RestoreLoop.cs

@ -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;
}
}
}

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

@ -29,19 +29,6 @@ namespace Decompiler.Transforms @@ -29,19 +29,6 @@ namespace Decompiler.Transforms
{
if (node == null)
return;
for (int i = 0; i < 4; i++) {
context.CancellationToken.ThrowIfCancellationRequested();
if (Options.ReduceAstJumps) {
node.AcceptVisitor(new Transforms.Ast.RemoveGotos(), null);
node.AcceptVisitor(new Transforms.Ast.RemoveDeadLabels(), null);
}
if (Options.ReduceAstLoops) {
node.AcceptVisitor(new Transforms.Ast.RestoreLoop(), null);
}
if (Options.ReduceAstOther) {
node.AcceptVisitor(new Transforms.Ast.RemoveEmptyElseBody(), null);
}
}
foreach (var transform in CreatePipeline(context)) {
context.CancellationToken.ThrowIfCancellationRequested();

6
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -63,10 +63,6 @@ @@ -63,10 +63,6 @@
<Compile Include="Ast\Transforms\DelegateConstruction.cs" />
<Compile Include="Ast\Transforms\ReplaceMethodCallsWithOperators.cs" />
<Compile Include="Ast\Transforms\PushNegation.cs" />
<Compile Include="Ast\Transforms\RemoveDeadLabels.cs" />
<Compile Include="Ast\Transforms\RemoveEmptyElseBody.cs" />
<Compile Include="Ast\Transforms\RemoveGotos.cs" />
<Compile Include="Ast\Transforms\RestoreLoop.cs" />
<Compile Include="Ast\Transforms\TransformationPipeline.cs" />
<Compile Include="Ast\Transforms\PatternStatementTransform.cs" />
<Compile Include="CecilExtensions.cs" />
@ -93,6 +89,7 @@ @@ -93,6 +89,7 @@
<Compile Include="FlowAnalysis\TransformToSsa.cs" />
<Compile Include="GraphVizGraph.cs" />
<Compile Include="ILAst\ArrayInitializers.cs" />
<Compile Include="ILAst\GotoRemoval.cs" />
<Compile Include="ILAst\ILAstBuilder.cs" />
<Compile Include="ILAst\ILAstOptimizer.cs" />
<Compile Include="ILAst\ILAstTypes.cs" />
@ -101,7 +98,6 @@ @@ -101,7 +98,6 @@
<Compile Include="ILAst\Pattern.cs" />
<Compile Include="ILAst\TypeAnalysis.cs" />
<Compile Include="ITextOutput.cs" />
<Compile Include="Options.cs" />
<Compile Include="PlainTextOutput.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TextOutputWriter.cs" />

202
ICSharpCode.Decompiler/ILAst/GotoRemoval.cs

@ -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 void 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
foreach (ILExpression gotoExpr in method.GetSelfAndChildrenRecursive<ILExpression>().Where(e => e.Code == ILCode.Br || e.Code == ILCode.Leave)) {
TrySimplifyGoto(gotoExpr);
}
// Remove dead lables and nops
RemoveDeadLabels(method);
// Remove redundant continue
foreach(ILWhileLoop loop in method.GetSelfAndChildrenRecursive<ILWhileLoop>()) {
var body = loop.BodyBlock.Body;
if (body.Count > 0 && body.Last().Match(ILCode.LoopContinue)) {
body.RemoveAt(body.Count - 1);
}
}
// Remove redundant return
if (method.Body.Count > 0 && method.Body.Last().Match(ILCode.Ret) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) {
method.Body.RemoveAt(method.Body.Count - 1);
}
}
public static void RemoveDeadLabels(ILNode method)
{
HashSet<ILLabel> liveLabels = new HashSet<ILLabel>(method.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.GetBranchTargets()));
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
block.Body = block.Body.Where(n => !n.Match(ILCode.Nop) && !(n is ILLabel && !liveLabels.Contains((ILLabel)n))).ToList();
}
}
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());
}
}
}

168
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -2,10 +2,10 @@ using System; @@ -2,10 +2,10 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.FlowAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.CSharp;
namespace Decompiler.ControlFlow
{
@ -16,8 +16,8 @@ namespace Decompiler.ControlFlow @@ -16,8 +16,8 @@ namespace Decompiler.ControlFlow
FindLoops,
FindConditions,
FlattenNestedMovableBlocks,
SimpleGotoRemoval,
RemoveDeadLabels,
GotoRemoval,
DuplicateReturns,
HandleArrayInitializers,
TypeInference,
None
@ -25,6 +25,8 @@ namespace Decompiler.ControlFlow @@ -25,6 +25,8 @@ namespace Decompiler.ControlFlow
public class ILAstOptimizer
{
int nextLabelIndex = 0;
Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>();
public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None)
@ -64,11 +66,12 @@ namespace Decompiler.ControlFlow @@ -64,11 +66,12 @@ namespace Decompiler.ControlFlow
if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return;
FlattenBasicBlocks(method);
if (abortBeforeStep == ILAstOptimizationStep.SimpleGotoRemoval) return;
SimpleGotoRemoval(method);
if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return;
new GotoRemoval().RemoveGotos(method);
if (abortBeforeStep == ILAstOptimizationStep.RemoveDeadLabels) return;
RemoveDeadLabels(method);
if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return;
DuplicateReturnStatements(method);
GotoRemoval.RemoveDeadLabels(method);
if (abortBeforeStep == ILAstOptimizationStep.HandleArrayInitializers) return;
ArrayInitializers.Transform(method);
@ -77,8 +80,6 @@ namespace Decompiler.ControlFlow @@ -77,8 +80,6 @@ namespace Decompiler.ControlFlow
TypeAnalysis.Run(context, method);
}
int nextBlockIndex = 0;
/// <summary>
/// Group input into a set of blocks that can be later arbitraliby schufled.
/// The method adds necessary branches to make control flow between blocks
@ -93,7 +94,7 @@ namespace Decompiler.ControlFlow @@ -93,7 +94,7 @@ namespace Decompiler.ControlFlow
List<ILNode> basicBlocks = new List<ILNode>();
ILBasicBlock basicBlock = new ILBasicBlock() {
EntryLabel = new ILLabel() { Name = "Block_" + (nextBlockIndex++) }
EntryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) }
};
basicBlocks.Add(basicBlock);
block.EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel);
@ -120,7 +121,7 @@ namespace Decompiler.ControlFlow @@ -120,7 +121,7 @@ namespace Decompiler.ControlFlow
// Insert as entry label
basicBlock.EntryLabel = (ILLabel)currNode;
} else {
basicBlock.EntryLabel = new ILLabel() { Name = "Block_" + (nextBlockIndex++) };
basicBlock.EntryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) };
basicBlock.Body.Add(currNode);
}
@ -176,11 +177,12 @@ namespace Decompiler.ControlFlow @@ -176,11 +177,12 @@ namespace Decompiler.ControlFlow
do {
modified = false;
for (int i = 0; i < block.Body.Count;) {
if (TrySimplifyShortCircuit(block.Body, (ILBasicBlock)block.Body[i])) {
ILBasicBlock bb = (ILBasicBlock)block.Body[i];
if (TrySimplifyShortCircuit(block.Body, bb)) {
modified = true;
continue;
}
if (TrySimplifyTernaryOperator(block.Body, (ILBasicBlock)block.Body[i])) {
if (TrySimplifyTernaryOperator(block.Body, bb)) {
modified = true;
continue;
}
@ -323,6 +325,53 @@ namespace Decompiler.ControlFlow @@ -323,6 +325,53 @@ namespace Decompiler.ControlFlow
return false;
}
void DuplicateReturnStatements(ILBlock method)
{
Dictionary<ILLabel, ILNode> nextSibling = new Dictionary<ILLabel, ILNode>();
// Build navigation data
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count - 1; i++) {
ILLabel curr = block.Body[i] as ILLabel;
if (curr != null) {
nextSibling[curr] = block.Body[i + 1];
}
}
}
// Duplicate returns
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count; i++) {
ILLabel targetLabel;
if (block.Body[i].Match(ILCode.Br, out targetLabel) ||
block.Body[i].Match(ILCode.Leave, out targetLabel))
{
// Skip extra labels
while(nextSibling.ContainsKey(targetLabel) && nextSibling[targetLabel] is ILLabel) {
targetLabel = (ILLabel)nextSibling[targetLabel];
}
// Inline return statement
ILNode target;
ILExpression retExpr;
if (nextSibling.TryGetValue(targetLabel, out target) &&
target.Match(ILCode.Ret, out retExpr))
{
ILVariable locVar;
object constValue;
if (retExpr.Arguments.Count == 0) {
block.Body[i] = new ILExpression(ILCode.Ret, null);
} else if (retExpr.Arguments.Single().Match(ILCode.Ldloc, out locVar)) {
block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldloc, locVar));
} else if (retExpr.Arguments.Single().Match(ILCode.Ldc_I4, out constValue)) {
block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldc_I4, constValue));
}
}
}
}
}
}
ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel)
{
int index = 0;
@ -400,33 +449,47 @@ namespace Decompiler.ControlFlow @@ -400,33 +449,47 @@ namespace Decompiler.ControlFlow
{
HashSet<ControlFlowNode> loopContents = FindLoopContent(scope, node);
ILWhileLoop loop = new ILWhileLoop();
ILBasicBlock basicBlock = node.UserData as ILBasicBlock;
ILBasicBlock basicBlock = (ILBasicBlock)node.UserData;
ILExpression branchExpr = null;
ILLabel trueLabel = null;
ILLabel falseLabel = null;
if(basicBlock != null && IsConditionalBranch(basicBlock, ref branchExpr, ref trueLabel, ref falseLabel)) {
if(IsConditionalBranch(basicBlock, ref branchExpr, ref trueLabel, ref falseLabel)) {
loopContents.Remove(node);
scope.Remove(node);
branchExpr.Operand = null; // Do not keep label alive
// Use loop to implement condition
loop.Condition = branchExpr;
loop.PreLoopLabel = basicBlock.EntryLabel;
loop.PostLoopGoto = new ILExpression(ILCode.Br, falseLabel);
loop.BodyBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) };
// Use loop to implement the condition
result.Add(new ILBasicBlock() {
EntryLabel = basicBlock.EntryLabel,
Body = new List<ILNode>() {
new ILWhileLoop() {
Condition = branchExpr,
BodyBlock = new ILBlock() {
EntryGoto = new ILExpression(ILCode.Br, trueLabel),
Body = FindLoops(loopContents, node, true)
}
},
new ILExpression(ILCode.Br, falseLabel)
},
FallthoughGoto = null
});
} else {
// Give the block some explicit entry point
ILLabel entryLabel = new ILLabel() { Name = "Loop_" + (nextBlockIndex++) };
loop.BodyBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, entryLabel) };
((ILBasicBlock)node.UserData).Body.Insert(0, entryLabel);
result.Add(new ILBasicBlock() {
EntryLabel = new ILLabel() { Name = "Loop_" + (nextLabelIndex++) },
Body = new List<ILNode>() {
new ILWhileLoop() {
BodyBlock = new ILBlock() {
EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel),
Body = FindLoops(loopContents, node, true)
}
},
},
FallthoughGoto = null
});
}
loop.BodyBlock.Body = FindLoops(loopContents, node, true);
// Move the content into loop block
scope.ExceptWith(loopContents);
result.Add(loop);
}
// Using the dominator tree should ensure we find the the widest loop first
@ -690,44 +753,31 @@ namespace Decompiler.ControlFlow @@ -690,44 +753,31 @@ namespace Decompiler.ControlFlow
}
}
}
}
void SimpleGotoRemoval(ILBlock ast)
public static class ILAstOptimizerExtensionMethods
{
// TODO: Assign IL ranges from br to something else
var blocks = ast.GetSelfAndChildrenRecursive<ILBlock>().ToList();
foreach(ILBlock block in blocks) {
for (int i = 0; i < block.Body.Count; i++) {
ILExpression expr = block.Body[i] as ILExpression;
// Uncoditional branch
if (expr != null && (expr.Code == ILCode.Br)) {
// Check that branch is followed by its label (allow multiple labels)
for (int j = i + 1; j < block.Body.Count; j++) {
ILLabel label = block.Body[j] as ILLabel;
if (label == null)
break; // Can not optimize
if (expr.Operand == label) {
block.Body.RemoveAt(i);
break; // Branch removed
}
}
}
}
public static bool Match(this ILNode node, ILCode code)
{
ILExpression expr = node as ILExpression;
return expr != null && expr.Prefixes == null && expr.Code == code;
}
public static bool Match(this ILNode node, ILCode code, out ILExpression expr)
{
expr = node as ILExpression;
return expr != null && expr.Prefixes == null && expr.Code == code;
}
void RemoveDeadLabels(ILBlock ast)
public static bool Match<T>(this ILNode node, ILCode code, out T operand)
{
HashSet<ILLabel> liveLabels = new HashSet<ILLabel>(ast.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.GetBranchTargets()));
var blocks = ast.GetSelfAndChildrenRecursive<ILBlock>().ToList();
foreach(ILBlock block in blocks) {
for (int i = 0; i < block.Body.Count;) {
ILLabel label = block.Body[i] as ILLabel;
if (label != null && !liveLabels.Contains(label)) {
block.Body.RemoveAt(i);
ILExpression expr = node as ILExpression;
if (expr != null && expr.Prefixes == null && expr.Code == code) {
operand = (T)expr.Operand;
return true;
} else {
i++;
}
}
operand = default(T);
return false;
}
}
}

26
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -18,7 +18,19 @@ namespace Decompiler @@ -18,7 +18,19 @@ namespace Decompiler
{
public IEnumerable<T> GetSelfAndChildrenRecursive<T>() where T: ILNode
{
return TreeTraversal.PreOrder(this, c => c != null ? c.GetChildren() : null).OfType<T>();
List<T> result = new List<T>(16);
AccumulateSelfAndChildrenRecursive(result);
return result;
}
void AccumulateSelfAndChildrenRecursive<T>(List<T> list) where T:ILNode
{
if (this is T)
list.Add((T)this);
foreach (ILNode node in this.GetChildren()) {
if (node != null)
node.AccumulateSelfAndChildrenRecursive(list);
}
}
public virtual IEnumerable<ILNode> GetChildren()
@ -360,27 +372,19 @@ namespace Decompiler @@ -360,27 +372,19 @@ namespace Decompiler
public class ILWhileLoop : ILNode
{
public ILLabel PreLoopLabel; // Label allowing to jump to condition
public ILExpression Condition;
public ILBlock BodyBlock; // BodyBlock.EntryGoto performs the goto for a met condition
public ILExpression PostLoopGoto; // Performs the goto for a failed condition
public ILBlock BodyBlock;
public override IEnumerable<ILNode> GetChildren()
{
if (this.PreLoopLabel != null)
yield return this.PreLoopLabel;
if (this.Condition != null)
yield return this.Condition;
if (this.BodyBlock != null)
yield return this.BodyBlock;
if (this.PostLoopGoto != null)
yield return this.PostLoopGoto;
}
public override void WriteTo(ITextOutput output)
{
if (this.PreLoopLabel != null)
this.PreLoopLabel.WriteTo(output);
output.WriteLine("");
output.Write("loop (");
if (this.Condition != null)
@ -390,8 +394,6 @@ namespace Decompiler @@ -390,8 +394,6 @@ namespace Decompiler
this.BodyBlock.WriteTo(output);
output.Unindent();
output.WriteLine("}");
if (this.PostLoopGoto != null)
this.PostLoopGoto.WriteTo(output);
}
}

2
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -260,6 +260,8 @@ namespace Decompiler @@ -260,6 +260,8 @@ namespace Decompiler
BrLogicOr,
InitArray, // Array Initializer
TernaryOp, // ?:
LoopBreak,
LoopContinue,
Pattern // used for ILAst pattern nodes
}

14
ICSharpCode.Decompiler/Options.cs

@ -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…
Cancel
Save