.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

183 lines
5.6 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
namespace Decompiler.Transforms.Ast
{
public class RemoveGotos: DepthFirstAstVisitor<object, object>
{
Stack<ForStatement> enteredLoops = new Stack<ForStatement>();
ForStatement CurrentLoop {
get {
if (enteredLoops.Count > 0) {
return enteredLoops.Peek();
} 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 &&
(CurrentLoop.EmbeddedStatement is BlockStatement) &&
((CurrentLoop.EmbeddedStatement as BlockStatement).LastChild as LabelStatement) != null &&
((CurrentLoop.EmbeddedStatement as BlockStatement).LastChild 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;
}
}
}