Browse Source

Support for "break" in switch statements

pull/70/head
David Srbecký 15 years ago
parent
commit
fd1594996b
  1. 2
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 35
      ICSharpCode.Decompiler/ILAst/GotoRemoval.cs
  3. 4
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  4. 2
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  5. 2
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

2
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -316,7 +316,7 @@ namespace ICSharpCode.Decompiler.Ast
new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name) new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name)
} }
}; };
case ILCode.LoopBreak: return new Ast.BreakStatement(); case ILCode.LoopOrSwitchBreak: return new Ast.BreakStatement();
case ILCode.LoopContinue: return new Ast.ContinueStatement(); case ILCode.LoopContinue: return new Ast.ContinueStatement();
#endregion #endregion
#region Conversions #region Conversions

35
ICSharpCode.Decompiler/ILAst/GotoRemoval.cs

@ -56,6 +56,19 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
// Remove redundant break at the end of case
foreach(ILSwitch ilSwitch in method.GetSelfAndChildrenRecursive<ILSwitch>()) {
foreach(ILBlock ilCase in ilSwitch.CaseBlocks) {
int count = ilCase.Body.Count;
if (count >= 2) {
if (!ilCase.Body[count - 2].CanFallthough() &&
ilCase.Body[count - 1].Match(ILCode.LoopOrSwitchBreak)) {
ilCase.Body.RemoveAt(count - 1);
}
}
}
}
// Remove redundant return // Remove redundant return
if (method.Body.Count > 0 && method.Body.Last().Match(ILCode.Ret) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) { 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); method.Body.RemoveAt(method.Body.Count - 1);
@ -98,21 +111,15 @@ namespace ICSharpCode.Decompiler.ILAst
return true; return true;
} }
// TODO: Swich also qualifies for break; ILNode breakBlock = GetParents(gotoExpr).Where(n => n is ILWhileLoop || n is ILSwitch).FirstOrDefault();
ILWhileLoop loop = null; if (breakBlock != null && target == Exit(breakBlock, new HashSet<ILNode>() { gotoExpr })) {
ILNode current = gotoExpr; gotoExpr.Code = ILCode.LoopOrSwitchBreak;
while(loop == null && current != null) {
current = parent[current];
loop = current as ILWhileLoop;
}
if (loop != null && target == Exit(loop, new HashSet<ILNode>() { gotoExpr })) {
gotoExpr.Code = ILCode.LoopBreak;
gotoExpr.Operand = null; gotoExpr.Operand = null;
return true; return true;
} }
if (loop != null && target == Enter(loop, new HashSet<ILNode>() { gotoExpr })) { ILNode continueBlock = GetParents(gotoExpr).Where(n => n is ILWhileLoop).FirstOrDefault();
if (continueBlock != null && target == Enter(continueBlock, new HashSet<ILNode>() { gotoExpr })) {
gotoExpr.Code = ILCode.LoopContinue; gotoExpr.Code = ILCode.LoopContinue;
gotoExpr.Operand = null; gotoExpr.Operand = null;
return true; return true;
@ -172,6 +179,12 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} else if (expr.Code == ILCode.Nop) { } else if (expr.Code == ILCode.Nop) {
return Exit(expr, visitedNodes); return Exit(expr, visitedNodes);
} else if (expr.Code == ILCode.LoopOrSwitchBreak) {
ILNode breakBlock = GetParents(expr).Where(n => n is ILWhileLoop || n is ILSwitch).First();
return Exit(breakBlock, new HashSet<ILNode>() { expr });
} else if (expr.Code == ILCode.LoopContinue) {
ILNode continueBlock = GetParents(expr).Where(n => n is ILWhileLoop).First();
return Enter(continueBlock, new HashSet<ILNode>() { expr });
} else { } else {
return expr; return expr;
} }

4
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -622,6 +622,8 @@ namespace ICSharpCode.Decompiler.ILAst
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, condTarget); HashSet<ControlFlowNode> content = FindDominatedNodes(scope, condTarget);
scope.ExceptWith(content); scope.ExceptWith(content);
caseBlock.Body.AddRange(FindConditions(content, condTarget)); caseBlock.Body.AddRange(FindConditions(content, condTarget));
// Add explicit break which should not be used by default, but the goto removal might decide to use it
caseBlock.Body.Add(new ILBasicBlock() { Body = { new ILExpression(ILCode.LoopOrSwitchBreak, null) } });
} }
ilSwitch.CaseBlocks.Add(caseBlock); ilSwitch.CaseBlocks.Add(caseBlock);
} }
@ -863,7 +865,7 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Throw: case ILCode.Throw:
case ILCode.Rethrow: case ILCode.Rethrow:
case ILCode.LoopContinue: case ILCode.LoopContinue:
case ILCode.LoopBreak: case ILCode.LoopOrSwitchBreak:
return false; return false;
} }
} }

2
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -260,7 +260,7 @@ namespace ICSharpCode.Decompiler.ILAst
LogicOr, LogicOr,
InitArray, // Array Initializer InitArray, // Array Initializer
TernaryOp, // ?: TernaryOp, // ?:
LoopBreak, LoopOrSwitchBreak,
LoopContinue, LoopContinue,
Ldc_Decimal, Ldc_Decimal,

2
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -456,6 +456,8 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Switch: case ILCode.Switch:
case ILCode.Throw: case ILCode.Throw:
case ILCode.Rethrow: case ILCode.Rethrow:
case ILCode.LoopOrSwitchBreak:
case ILCode.LoopContinue:
return null; return null;
case ILCode.Ret: case ILCode.Ret:
if (forceInferChildren && expr.Arguments.Count == 1) if (forceInferChildren && expr.Arguments.Count == 1)

Loading…
Cancel
Save