From aab0e43a0804b74e21834f78a281b99db55a5626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sat, 5 Mar 2011 20:12:55 +0000 Subject: [PATCH 01/10] Don't just fall out of case statements. --- ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs | 2 -- ICSharpCode.Decompiler/ILAst/GotoRemoval.cs | 10 ++++++---- ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs | 8 +++----- ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 3 --- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index b25fe1cb5d..a143d8e737 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -130,8 +130,6 @@ IEnumerable TransformNode(ILNode node) switchStmt.SwitchSections.Add(section); } yield return switchStmt; - if (ilSwitch.DefaultGoto != null) - yield return (Statement)TransformExpression(ilSwitch.DefaultGoto); } else if (node is ILTryCatchBlock) { ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node); var tryCatchStmt = new Ast.TryCatchStatement(); diff --git a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs index 253d01b05e..4b5c09cbec 100644 --- a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs +++ b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs @@ -76,6 +76,7 @@ bool TrySimplifyGoto(ILExpression gotoExpr) return true; } + // TODO: Swich also qualifies for break; ILWhileLoop loop = null; ILNode current = gotoExpr; while(loop == null && current != null) { @@ -184,13 +185,14 @@ ILExpression Exit(ILNode node, HashSet visitedNodes) } } - if (nodeParent is ILCondition || - nodeParent is ILTryCatchBlock || - nodeParent is ILSwitch) - { + if (nodeParent is ILCondition || nodeParent is ILTryCatchBlock) { return Exit(nodeParent, visitedNodes); } + if (nodeParent is ILSwitch) { + return null; // Implicit exit from switch is not allowed + } + if (nodeParent is ILWhileLoop) { return Enter(nodeParent, visitedNodes); } diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 9ddaa6688f..36c0756c43 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -586,13 +586,11 @@ List FindConditions(HashSet scope, ControlFlowNode entr // The labels will not be used - kill them condBranch.Operand = null; - ILSwitch ilSwitch = new ILSwitch() { - Condition = condBranch, - DefaultGoto = block.FallthoughGoto - }; + ILSwitch ilSwitch = new ILSwitch() { Condition = condBranch }; result.Add(new ILBasicBlock() { EntryLabel = block.EntryLabel, // Keep the entry label - Body = { ilSwitch } + Body = { ilSwitch }, + FallthoughGoto = block.FallthoughGoto }); // Remove the item so that it is not picked up as content diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index 6227fb92e6..cc7a65e049 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -443,7 +443,6 @@ public class ILSwitch: ILNode { public ILExpression Condition; public List CaseBlocks = new List(); - public ILExpression DefaultGoto; public override IEnumerable GetChildren() { @@ -452,8 +451,6 @@ public override IEnumerable GetChildren() foreach (ILBlock caseBlock in this.CaseBlocks) { yield return caseBlock; } - if (this.DefaultGoto != null) - yield return this.DefaultGoto; } public override void WriteTo(ITextOutput output) From f0faaaf8d20d35e95c63b58853abed19ff297531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sat, 5 Mar 2011 23:23:53 +0000 Subject: [PATCH 02/10] Do not enter try-blocks in the goto removal algorithm --- ICSharpCode.Decompiler/ILAst/GotoRemoval.cs | 79 ++++++++++++++++++--- 1 file changed, 68 insertions(+), 11 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs index 4b5c09cbec..1c083389d4 100644 --- a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs +++ b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs @@ -29,9 +29,13 @@ public void RemoveGotos(ILBlock method) } // Simplify gotos - foreach (ILExpression gotoExpr in method.GetSelfAndChildrenRecursive().Where(e => e.Code == ILCode.Br || e.Code == ILCode.Leave)) { - TrySimplifyGoto(gotoExpr); - } + bool modified; + do { + modified = false; + foreach (ILExpression gotoExpr in method.GetSelfAndChildrenRecursive().Where(e => e.Code == ILCode.Br || e.Code == ILCode.Leave)) { + modified |= TrySimplifyGoto(gotoExpr); + } + } while(modified); RemoveRedundantCode(method); } @@ -58,20 +62,38 @@ public static void RemoveRedundantCode(ILBlock method) } } + IEnumerable GetParents(ILNode node) + { + ILNode current = node; + while(true) { + current = parent[current]; + if (current == null) + yield break; + yield return current; + } + } + 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 target = Enter(gotoExpr, new HashSet()); if (target == null) return false; + // The gotoExper is marked as visited because we do not want to + // walk over node which we plan to modify + + // The simulated path always has to start in the same try-block + // in other for the same finally blocks to be executed. + if (target == Exit(gotoExpr, new HashSet() { gotoExpr })) { gotoExpr.Code = ILCode.Nop; gotoExpr.Operand = null; - target.ILRanges.AddRange(gotoExpr.ILRanges); + if (target is ILExpression) + ((ILExpression)target).ILRanges.AddRange(gotoExpr.ILRanges); gotoExpr.ILRanges.Clear(); return true; } @@ -100,9 +122,10 @@ bool TrySimplifyGoto(ILExpression gotoExpr) } /// - /// Get the first expression to be excecuted if the instruction pointer is at the start of the given node + /// Get the first expression to be excecuted if the instruction pointer is at the start of the given node. + /// Try blocks may not be entered in any way. If possible, the try block is returned as the node to be executed. /// - ILExpression Enter(ILNode node, HashSet visitedNodes) + ILNode Enter(ILNode node, HashSet visitedNodes) { if (node == null) throw new ArgumentNullException(); @@ -118,7 +141,35 @@ ILExpression Enter(ILNode node, HashSet visitedNodes) ILExpression expr = node as ILExpression; if (expr != null) { if (expr.Code == ILCode.Br || expr.Code == ILCode.Leave) { - return Enter((ILLabel)expr.Operand, visitedNodes); + ILLabel target = (ILLabel)expr.Operand; + // Early exit - same try-block + if (GetParents(expr).OfType().FirstOrDefault() == GetParents(target).OfType().FirstOrDefault()) + return Enter(target, visitedNodes); + // Make sure we are not entering any try-block + var srcTryBlocks = GetParents(expr).OfType().Reverse().ToList(); + var dstTryBlocks = GetParents(target).OfType().Reverse().ToList(); + // Skip blocks that we are already in + int i = 0; + while(i < srcTryBlocks.Count && i < dstTryBlocks.Count && srcTryBlocks[i] == dstTryBlocks[i]) i++; + if (i == dstTryBlocks.Count) { + return Enter(target, visitedNodes); + } else { + ILTryCatchBlock dstTryBlock = dstTryBlocks[i]; + // Check that the goto points to the start + ILTryCatchBlock current = dstTryBlock; + while(current != null) { + foreach(ILNode n in current.TryBlock.Body) { + if (n is ILLabel) { + if (n == target) + return dstTryBlock; + } else if (!n.Match(ILCode.Nop)) { + current = n as ILTryCatchBlock; + break; + } + } + } + return null; + } } else if (expr.Code == ILCode.Nop) { return Exit(expr, visitedNodes); } else { @@ -153,7 +204,7 @@ ILExpression Enter(ILNode node, HashSet visitedNodes) ILTryCatchBlock tryCatch = node as ILTryCatchBlock; if (tryCatch != null) { - return Enter(tryCatch.TryBlock, visitedNodes); + return tryCatch; } ILSwitch ilSwitch = node as ILSwitch; @@ -167,7 +218,7 @@ ILExpression Enter(ILNode node, HashSet visitedNodes) /// /// Get the first expression to be excecuted if the instruction pointer is at the end of the given node /// - ILExpression Exit(ILNode node, HashSet visitedNodes) + ILNode Exit(ILNode node, HashSet visitedNodes) { if (node == null) throw new ArgumentNullException(); @@ -185,7 +236,13 @@ ILExpression Exit(ILNode node, HashSet visitedNodes) } } - if (nodeParent is ILCondition || nodeParent is ILTryCatchBlock) { + if (nodeParent is ILCondition) { + return Exit(nodeParent, visitedNodes); + } + + if (nodeParent is ILTryCatchBlock) { + // Finally blocks are completely ignored. + // We rely on the fact that try blocks can not be entered. return Exit(nodeParent, visitedNodes); } From fd1594996bb1839c57b42f56e116c1726312b818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 6 Mar 2011 10:05:12 +0000 Subject: [PATCH 03/10] Support for "break" in switch statements --- .../Ast/AstMethodBodyBuilder.cs | 2 +- ICSharpCode.Decompiler/ILAst/GotoRemoval.cs | 35 +++++++++++++------ .../ILAst/ILAstOptimizer.cs | 4 ++- ICSharpCode.Decompiler/ILAst/ILCodes.cs | 2 +- ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs | 2 ++ 5 files changed, 31 insertions(+), 14 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index a143d8e737..da7ad22af5 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -316,7 +316,7 @@ AstNode TransformByteCode(ILExpression byteCode) 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(); #endregion #region Conversions diff --git a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs index 1c083389d4..16b2c48b81 100644 --- a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs +++ b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs @@ -56,6 +56,19 @@ public static void RemoveRedundantCode(ILBlock method) } } + // Remove redundant break at the end of case + foreach(ILSwitch ilSwitch in method.GetSelfAndChildrenRecursive()) { + 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 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); @@ -98,21 +111,15 @@ bool TrySimplifyGoto(ILExpression gotoExpr) return true; } - // TODO: Swich also qualifies for break; - 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() { gotoExpr })) { - gotoExpr.Code = ILCode.LoopBreak; + ILNode breakBlock = GetParents(gotoExpr).Where(n => n is ILWhileLoop || n is ILSwitch).FirstOrDefault(); + if (breakBlock != null && target == Exit(breakBlock, new HashSet() { gotoExpr })) { + gotoExpr.Code = ILCode.LoopOrSwitchBreak; gotoExpr.Operand = null; return true; } - if (loop != null && target == Enter(loop, new HashSet() { gotoExpr })) { + ILNode continueBlock = GetParents(gotoExpr).Where(n => n is ILWhileLoop).FirstOrDefault(); + if (continueBlock != null && target == Enter(continueBlock, new HashSet() { gotoExpr })) { gotoExpr.Code = ILCode.LoopContinue; gotoExpr.Operand = null; return true; @@ -172,6 +179,12 @@ ILNode Enter(ILNode node, HashSet visitedNodes) } } else if (expr.Code == ILCode.Nop) { 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() { expr }); + } else if (expr.Code == ILCode.LoopContinue) { + ILNode continueBlock = GetParents(expr).Where(n => n is ILWhileLoop).First(); + return Enter(continueBlock, new HashSet() { expr }); } else { return expr; } diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 36c0756c43..ece71a1e90 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -622,6 +622,8 @@ List FindConditions(HashSet scope, ControlFlowNode entr HashSet content = FindDominatedNodes(scope, condTarget); scope.ExceptWith(content); 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); } @@ -863,7 +865,7 @@ public static bool CanFallthough(this ILNode node) case ILCode.Throw: case ILCode.Rethrow: case ILCode.LoopContinue: - case ILCode.LoopBreak: + case ILCode.LoopOrSwitchBreak: return false; } } diff --git a/ICSharpCode.Decompiler/ILAst/ILCodes.cs b/ICSharpCode.Decompiler/ILAst/ILCodes.cs index 1d3201d3b8..43acc361ec 100644 --- a/ICSharpCode.Decompiler/ILAst/ILCodes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILCodes.cs @@ -260,7 +260,7 @@ public enum ILCode LogicOr, InitArray, // Array Initializer TernaryOp, // ?: - LoopBreak, + LoopOrSwitchBreak, LoopContinue, Ldc_Decimal, diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index bed175b198..b7afed8d4c 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -456,6 +456,8 @@ TypeReference DoInferTypeForExpression(ILExpression expr, TypeReference expected case ILCode.Switch: case ILCode.Throw: case ILCode.Rethrow: + case ILCode.LoopOrSwitchBreak: + case ILCode.LoopContinue: return null; case ILCode.Ret: if (forceInferChildren && expr.Arguments.Count == 1) From 74b6624c5e76aa77899463bab969a9a693d36f53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 6 Mar 2011 11:20:58 +0000 Subject: [PATCH 04/10] Support multiple values per case block --- .../Ast/AstMethodBodyBuilder.cs | 8 +-- ICSharpCode.Decompiler/ILAst/GotoRemoval.cs | 2 + .../ILAst/ILAstOptimizer.cs | 50 ++++++++++--------- ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 26 +++++++--- 4 files changed, 52 insertions(+), 34 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index da7ad22af5..143789f363 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -122,11 +122,11 @@ IEnumerable TransformNode(ILNode node) }; } else if (node is ILSwitch) { ILSwitch ilSwitch = (ILSwitch)node; - SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition.Arguments[0]) }; - for (int i = 0; i < ilSwitch.CaseBlocks.Count; i++) { + SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition) }; + foreach (var caseBlock in ilSwitch.CaseBlocks) { SwitchSection section = new SwitchSection(); - section.CaseLabels.Add(new CaseLabel() { Expression = new PrimitiveExpression(i) }); - section.Statements.Add(TransformBlock(ilSwitch.CaseBlocks[i])); + section.CaseLabels.AddRange(caseBlock.Values.Select(i => new CaseLabel() { Expression = new PrimitiveExpression(i) })); + section.Statements.Add(TransformBlock(caseBlock)); switchStmt.SwitchSections.Add(section); } yield return switchStmt; diff --git a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs index 16b2c48b81..6db1374d22 100644 --- a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs +++ b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs @@ -57,6 +57,7 @@ public static void RemoveRedundantCode(ILBlock method) } // Remove redundant break at the end of case + // Remove redundant case blocks altogether foreach(ILSwitch ilSwitch in method.GetSelfAndChildrenRecursive()) { foreach(ILBlock ilCase in ilSwitch.CaseBlocks) { int count = ilCase.Body.Count; @@ -67,6 +68,7 @@ public static void RemoveRedundantCode(ILBlock method) } } } + ilSwitch.CaseBlocks.RemoveAll(b => b.Body.Count == 1 && b.Body.Single().Match(ILCode.LoopOrSwitchBreak)); } // Remove redundant return diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index ece71a1e90..ff08893d03 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -101,6 +101,7 @@ void ReduceBranchInstructionSet(ILBlock block) ILExpression expr = block.Body[i] as ILExpression; if (expr != null && expr.Prefixes == null) { switch(expr.Code) { + case ILCode.Switch: case ILCode.Brtrue: expr.Arguments.Single().ILRanges.AddRange(expr.ILRanges); expr.ILRanges.Clear(); @@ -579,19 +580,15 @@ List FindConditions(HashSet scope, ControlFlowNode entr ILExpression condBranch = block.Body[0] as ILExpression; // Switch - if (condBranch != null && condBranch.Operand is ILLabel[] && condBranch.Arguments.Count > 0) { + ILLabel[] caseLabels; + if (condBranch.Match(ILCode.Switch, out caseLabels)) { - ILLabel[] caseLabels = (ILLabel[])condBranch.Operand; - - // The labels will not be used - kill them - condBranch.Operand = null; - - ILSwitch ilSwitch = new ILSwitch() { Condition = condBranch }; + ILSwitch ilSwitch = new ILSwitch() { Condition = condBranch.Arguments.Single() }; result.Add(new ILBasicBlock() { - EntryLabel = block.EntryLabel, // Keep the entry label - Body = { ilSwitch }, - FallthoughGoto = block.FallthoughGoto - }); + EntryLabel = block.EntryLabel, // Keep the entry label + Body = { ilSwitch }, + FallthoughGoto = block.FallthoughGoto + }); // Remove the item so that it is not picked up as content scope.RemoveOrThrow(node); @@ -611,21 +608,26 @@ List FindConditions(HashSet scope, ControlFlowNode entr frontiers.UnionWith(condTarget.DominanceFrontier); } - foreach(ILLabel condLabel in caseLabels) { - ControlFlowNode condTarget = null; - labelToCfNode.TryGetValue(condLabel, out condTarget); + for (int i = 0; i < caseLabels.Length; i++) { + ILLabel condLabel = caseLabels[i]; - ILBlock caseBlock = new ILBlock() { - EntryGoto = new ILExpression(ILCode.Br, condLabel) - }; - if (condTarget != null && !frontiers.Contains(condTarget)) { - HashSet content = FindDominatedNodes(scope, condTarget); - scope.ExceptWith(content); - 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) } }); + // Find or create new case block + ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.Where(b => b.EntryGoto.Operand == condLabel).FirstOrDefault(); + if (caseBlock == null) { + caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, condLabel) }; + ilSwitch.CaseBlocks.Add(caseBlock); + + ControlFlowNode condTarget = null; + labelToCfNode.TryGetValue(condLabel, out condTarget); + if (condTarget != null && !frontiers.Contains(condTarget)) { + HashSet content = FindDominatedNodes(scope, condTarget); + scope.ExceptWith(content); + 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); + caseBlock.Values.Add(i); } } diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index cc7a65e049..8d4832fe83 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -441,8 +442,24 @@ public override void WriteTo(ITextOutput output) public class ILSwitch: ILNode { + public class CaseBlock: ILBlock + { + public List Values = new List(); + + public override void WriteTo(ITextOutput output) + { + Debug.Assert(Values.Count > 0); + foreach (int i in this.Values) { + output.WriteLine("case {0}:", i); + } + output.Indent(); + base.WriteTo(output); + output.Unindent(); + } + } + public ILExpression Condition; - public List CaseBlocks = new List(); + public List CaseBlocks = new List(); public override IEnumerable GetChildren() { @@ -459,11 +476,8 @@ public override void WriteTo(ITextOutput output) Condition.WriteTo(output); output.WriteLine(") {"); output.Indent(); - for (int i = 0; i < CaseBlocks.Count; i++) { - output.WriteLine("case {0}:", i); - output.Indent(); - CaseBlocks[i].WriteTo(output); - output.Unindent(); + foreach (CaseBlock caseBlock in this.CaseBlocks) { + caseBlock.WriteTo(output); } output.Unindent(); output.WriteLine("}"); From fe0b0130e406af99b1d6ecefbc421ed58312873a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 6 Mar 2011 11:51:20 +0000 Subject: [PATCH 05/10] Whitespace reformatting of TransformByteCode --- .../Ast/AstMethodBodyBuilder.cs | 418 ++++++++---------- 1 file changed, 189 insertions(+), 229 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 143789f363..75c6ad1cd3 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -79,9 +79,7 @@ Ast.BlockStatement TransformBlock(ILBlock block) { Ast.BlockStatement astBlock = new BlockStatement(); if (block != null) { - if (block.EntryGoto != null) - astBlock.Add((Statement)TransformExpression(block.EntryGoto)); - foreach(ILNode node in block.Body) { + foreach(ILNode node in block.GetChildren()) { astBlock.AddRange(TransformNode(node)); } } @@ -154,41 +152,6 @@ IEnumerable TransformNode(ILNode node) } } - List TransformExpressionArguments(ILExpression expr) - { - List args = new List(); - // Args generated by nested expressions (which must be closed) - foreach(ILExpression arg in expr.Arguments) { - args.Add((Ast.Expression)TransformExpression(arg)); - } - return args; - } - - static string FormatByteCodeOperand(object operand) - { - if (operand == null) { - return string.Empty; - //} else if (operand is ILExpression) { - // return string.Format("IL_{0:X2}", ((ILExpression)operand).Offset); - } else if (operand is MethodReference) { - return ((MethodReference)operand).Name + "()"; - } else if (operand is Cecil.TypeReference) { - return ((Cecil.TypeReference)operand).FullName; - } else if (operand is VariableDefinition) { - return ((VariableDefinition)operand).Name; - } else if (operand is ParameterDefinition) { - return ((ParameterDefinition)operand).Name; - } else if (operand is FieldReference) { - return ((FieldReference)operand).Name; - } else if (operand is string) { - return "\"" + operand + "\""; - } else if (operand is int) { - return operand.ToString(); - } else { - return operand.ToString(); - } - } - AstNode TransformExpression(ILExpression expr) { AstNode node = TransformByteCode(expr); @@ -204,57 +167,57 @@ AstNode TransformByteCode(ILExpression byteCode) object operand = byteCode.Operand; AstType operandAsTypeRef = AstBuilder.ConvertType(operand as Cecil.TypeReference); - List args = TransformExpressionArguments(byteCode); + List args = new List(); + foreach(ILExpression arg in byteCode.Arguments) { + args.Add((Ast.Expression)TransformExpression(arg)); + } Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; Ast.Expression arg3 = args.Count >= 3 ? args[2] : null; switch(byteCode.Code) { - #region Arithmetic - case ILCode.Add: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); - case ILCode.Add_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); - case ILCode.Add_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); - case ILCode.Div: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); - case ILCode.Div_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); - case ILCode.Mul: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); - case ILCode.Mul_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); - case ILCode.Mul_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); - case ILCode.Rem: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); - case ILCode.Rem_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); - case ILCode.Sub: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); - case ILCode.Sub_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); - case ILCode.Sub_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); - case ILCode.And: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2); - case ILCode.Or: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseOr, arg2); - case ILCode.Xor: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ExclusiveOr, arg2); - case ILCode.Shl: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2); - case ILCode.Shr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); - case ILCode.Shr_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); - - case ILCode.Neg: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Minus, arg1); - case ILCode.Not: return new Ast.UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1); - #endregion - #region Arrays + #region Arithmetic + case ILCode.Add: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); + case ILCode.Add_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); + case ILCode.Add_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); + case ILCode.Div: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); + case ILCode.Div_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); + case ILCode.Mul: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); + case ILCode.Mul_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); + case ILCode.Mul_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); + case ILCode.Rem: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); + case ILCode.Rem_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); + case ILCode.Sub: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); + case ILCode.Sub_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); + case ILCode.Sub_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); + case ILCode.And: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2); + case ILCode.Or: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseOr, arg2); + case ILCode.Xor: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ExclusiveOr, arg2); + case ILCode.Shl: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2); + case ILCode.Shr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); + case ILCode.Shr_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); + case ILCode.Neg: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Minus, arg1); + case ILCode.Not: return new Ast.UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1); + #endregion + #region Arrays case ILCode.Newarr: - case ILCode.InitArray: - { - var ace = new Ast.ArrayCreateExpression(); - ace.Type = operandAsTypeRef; - ComposedType ct = operandAsTypeRef as ComposedType; - if (ct != null) { - // change "new (int[,])[10] to new int[10][,]" - ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); - } - if (byteCode.Code == ILCode.InitArray) { - ace.Initializer = new ArrayInitializerExpression(); - ace.Initializer.Elements.AddRange(args); - } else { - ace.Arguments.Add(arg1); - } - return ace; + case ILCode.InitArray: { + var ace = new Ast.ArrayCreateExpression(); + ace.Type = operandAsTypeRef; + ComposedType ct = operandAsTypeRef as ComposedType; + if (ct != null) { + // change "new (int[,])[10] to new int[10][,]" + ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); } - case ILCode.Ldlen: - return arg1.Member("Length"); + if (byteCode.Code == ILCode.InitArray) { + ace.Initializer = new ArrayInitializerExpression(); + ace.Initializer.Elements.AddRange(args); + } else { + ace.Arguments.Add(arg1); + } + return ace; + } + case ILCode.Ldlen: return arg1.Member("Length"); case ILCode.Ldelem_I: case ILCode.Ldelem_I1: case ILCode.Ldelem_I2: @@ -268,9 +231,7 @@ AstNode TransformByteCode(ILExpression byteCode) case ILCode.Ldelem_Ref: case ILCode.Ldelem_Any: return arg1.Indexer(arg2); - case ILCode.Ldelema: - return MakeRef(arg1.Indexer(arg2)); - + case ILCode.Ldelema: return MakeRef(arg1.Indexer(arg2)); case ILCode.Stelem_I: case ILCode.Stelem_I1: case ILCode.Stelem_I2: @@ -281,33 +242,28 @@ AstNode TransformByteCode(ILExpression byteCode) case ILCode.Stelem_Ref: case ILCode.Stelem_Any: return new Ast.AssignmentExpression(arg1.Indexer(arg2), arg3); - #endregion - #region Comparison - case ILCode.Ceq: - return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); - case ILCode.Cgt: - return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); - case ILCode.Cgt_Un: + #endregion + #region Comparison + case ILCode.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); + case ILCode.Cgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); + case ILCode.Cgt_Un: { // can also mean Inequality, when used with object references - { - TypeReference arg1Type = byteCode.Arguments[0].InferredType; - if (arg1Type != null && !arg1Type.IsValueType) - return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); - else - return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); - } - case ILCode.Clt: - return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); - case ILCode.Clt_Un: - return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); - #endregion - #region Logical + TypeReference arg1Type = byteCode.Arguments[0].InferredType; + if (arg1Type != null && !arg1Type.IsValueType) + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); + else + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); + } + case ILCode.Clt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); + case ILCode.Clt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); + #endregion + #region Logical case ILCode.LogicNot: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); case ILCode.LogicAnd: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalAnd, arg2); case ILCode.LogicOr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalOr, arg2); case ILCode.TernaryOp: return new Ast.ConditionalExpression() { Condition = arg1, TrueExpression = arg2, FalseExpression = arg3 }; - #endregion - #region Branch + #endregion + #region Branch case ILCode.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name); case ILCode.Brtrue: return new Ast.IfElseStatement() { @@ -316,10 +272,10 @@ AstNode TransformByteCode(ILExpression byteCode) new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name) } }; - case ILCode.LoopOrSwitchBreak: return new Ast.BreakStatement(); - case ILCode.LoopContinue: return new Ast.ContinueStatement(); - #endregion - #region Conversions + case ILCode.LoopOrSwitchBreak: return new Ast.BreakStatement(); + case ILCode.LoopContinue: return new Ast.ContinueStatement(); + #endregion + #region Conversions case ILCode.Conv_I1: case ILCode.Conv_I2: case ILCode.Conv_I4: @@ -329,12 +285,11 @@ AstNode TransformByteCode(ILExpression byteCode) case ILCode.Conv_U4: case ILCode.Conv_U8: return arg1; // conversion is handled by Convert() function using the info from type analysis - case ILCode.Conv_I: return arg1.CastTo(typeof(IntPtr)); // TODO - case ILCode.Conv_U: return arg1.CastTo(typeof(UIntPtr)); // TODO - case ILCode.Conv_R4: return arg1.CastTo(typeof(float)); - case ILCode.Conv_R8: return arg1.CastTo(typeof(double)); - case ILCode.Conv_R_Un: return arg1.CastTo(typeof(double)); // TODO - + case ILCode.Conv_I: return arg1.CastTo(typeof(IntPtr)); // TODO + case ILCode.Conv_U: return arg1.CastTo(typeof(UIntPtr)); // TODO + case ILCode.Conv_R4: return arg1.CastTo(typeof(float)); + case ILCode.Conv_R8: return arg1.CastTo(typeof(double)); + case ILCode.Conv_R_Un: return arg1.CastTo(typeof(double)); // TODO case ILCode.Conv_Ovf_I1: case ILCode.Conv_Ovf_I2: case ILCode.Conv_Ovf_I4: @@ -352,22 +307,17 @@ AstNode TransformByteCode(ILExpression byteCode) case ILCode.Conv_Ovf_U4_Un: case ILCode.Conv_Ovf_U8_Un: return arg1; // conversion was handled by Convert() function using the info from type analysis - case ILCode.Conv_Ovf_I: return arg1.CastTo(typeof(IntPtr)); // TODO - case ILCode.Conv_Ovf_U: return arg1.CastTo(typeof(UIntPtr)); - case ILCode.Conv_Ovf_I_Un: return arg1.CastTo(typeof(IntPtr)); - case ILCode.Conv_Ovf_U_Un: return arg1.CastTo(typeof(UIntPtr)); - - case ILCode.Castclass: - case ILCode.Unbox_Any: - return arg1.CastTo(operandAsTypeRef); - case ILCode.Isinst: - return arg1.CastAs(operandAsTypeRef); - case ILCode.Box: - return arg1; - case ILCode.Unbox: - return InlineAssembly(byteCode, args); - #endregion - #region Indirect + case ILCode.Conv_Ovf_I: return arg1.CastTo(typeof(IntPtr)); // TODO + case ILCode.Conv_Ovf_U: return arg1.CastTo(typeof(UIntPtr)); + case ILCode.Conv_Ovf_I_Un: return arg1.CastTo(typeof(IntPtr)); + case ILCode.Conv_Ovf_U_Un: return arg1.CastTo(typeof(UIntPtr)); + case ILCode.Castclass: return arg1.CastTo(operandAsTypeRef); + case ILCode.Unbox_Any: return arg1.CastTo(operandAsTypeRef); + case ILCode.Isinst: return arg1.CastAs(operandAsTypeRef); + case ILCode.Box: return arg1; + case ILCode.Unbox: return InlineAssembly(byteCode, args); + #endregion + #region Indirect case ILCode.Ldind_I: case ILCode.Ldind_I1: case ILCode.Ldind_I2: @@ -384,7 +334,6 @@ AstNode TransformByteCode(ILExpression byteCode) return ((DirectionExpression)args[0]).Expression.Detach(); else return InlineAssembly(byteCode, args); - case ILCode.Stind_I: case ILCode.Stind_I1: case ILCode.Stind_I2: @@ -398,49 +347,43 @@ AstNode TransformByteCode(ILExpression byteCode) return new AssignmentExpression(((DirectionExpression)args[0]).Expression.Detach(), args[1]); else return InlineAssembly(byteCode, args); - #endregion - case ILCode.Arglist: return InlineAssembly(byteCode, args); - case ILCode.Break: return InlineAssembly(byteCode, args); - case ILCode.Call: - return TransformCall(false, operand, methodDef, args); - case ILCode.Callvirt: - return TransformCall(true, operand, methodDef, args); - case ILCode.Ldftn: - { - Cecil.MethodReference cecilMethod = ((MethodReference)operand); - var expr = new Ast.IdentifierExpression(cecilMethod.Name); - expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); - expr.AddAnnotation(cecilMethod); - return new IdentifierExpression("ldftn").Invoke(expr) - .WithAnnotation(new Transforms.DelegateConstruction.Annotation(false)); - } - case ILCode.Ldvirtftn: - { - Cecil.MethodReference cecilMethod = ((MethodReference)operand); - var expr = new Ast.IdentifierExpression(cecilMethod.Name); - expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); - expr.AddAnnotation(cecilMethod); - return new IdentifierExpression("ldvirtftn").Invoke(expr) - .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true)); - } - - case ILCode.Calli: return InlineAssembly(byteCode, args); - case ILCode.Ckfinite: return InlineAssembly(byteCode, args); - case ILCode.Constrained: return InlineAssembly(byteCode, args); - case ILCode.Cpblk: return InlineAssembly(byteCode, args); - case ILCode.Cpobj: return InlineAssembly(byteCode, args); - case ILCode.Dup: return arg1; - case ILCode.Endfilter: return InlineAssembly(byteCode, args); - case ILCode.Endfinally: return null; - case ILCode.Initblk: return InlineAssembly(byteCode, args); + #endregion + case ILCode.Arglist: return InlineAssembly(byteCode, args); + case ILCode.Break: return InlineAssembly(byteCode, args); + case ILCode.Call: return TransformCall(false, operand, methodDef, args); + case ILCode.Callvirt: return TransformCall(true, operand, methodDef, args); + case ILCode.Ldftn: { + Cecil.MethodReference cecilMethod = ((MethodReference)operand); + var expr = new Ast.IdentifierExpression(cecilMethod.Name); + expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); + expr.AddAnnotation(cecilMethod); + return new IdentifierExpression("ldftn").Invoke(expr) + .WithAnnotation(new Transforms.DelegateConstruction.Annotation(false)); + } + case ILCode.Ldvirtftn: { + Cecil.MethodReference cecilMethod = ((MethodReference)operand); + var expr = new Ast.IdentifierExpression(cecilMethod.Name); + expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); + expr.AddAnnotation(cecilMethod); + return new IdentifierExpression("ldvirtftn").Invoke(expr) + .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true)); + } + case ILCode.Calli: return InlineAssembly(byteCode, args); + case ILCode.Ckfinite: return InlineAssembly(byteCode, args); + case ILCode.Constrained: return InlineAssembly(byteCode, args); + case ILCode.Cpblk: return InlineAssembly(byteCode, args); + case ILCode.Cpobj: return InlineAssembly(byteCode, args); + case ILCode.Dup: return arg1; + case ILCode.Endfilter: return InlineAssembly(byteCode, args); + case ILCode.Endfinally: return null; + case ILCode.Initblk: return InlineAssembly(byteCode, args); case ILCode.Initobj: if (args[0] is DirectionExpression) return new AssignmentExpression(((DirectionExpression)args[0]).Expression.Detach(), new DefaultValueExpression { Type = operandAsTypeRef }); else return InlineAssembly(byteCode, args); - case ILCode.Jmp: - return InlineAssembly(byteCode, args); - case ILCode.Ldarg: + case ILCode.Jmp: return InlineAssembly(byteCode, args); + case ILCode.Ldarg: { if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) { if (context.CurrentMethod.DeclaringType.IsValueType) return MakeRef(new Ast.ThisReferenceExpression()); @@ -453,14 +396,14 @@ AstNode TransformByteCode(ILExpression byteCode) else return expr; } + } case ILCode.Ldarga: if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) { return MakeRef(new Ast.ThisReferenceExpression()); } else { return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand)); } - case ILCode.Ldc_I4: - return AstBuilder.MakePrimitive((int)operand, byteCode.InferredType); + case ILCode.Ldc_I4: return AstBuilder.MakePrimitive((int)operand, byteCode.InferredType); case ILCode.Ldc_I8: case ILCode.Ldc_R4: case ILCode.Ldc_R8: @@ -482,8 +425,7 @@ AstNode TransformByteCode(ILExpression byteCode) AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) .Member(((FieldReference)operand).Name).WithAnnotation(operand), arg1); - case ILCode.Ldflda: - return MakeRef(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand)); + case ILCode.Ldflda: return MakeRef(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand)); case ILCode.Ldsflda: return MakeRef( AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) @@ -494,68 +436,61 @@ AstNode TransformByteCode(ILExpression byteCode) case ILCode.Ldloca: localVariablesToDefine.Add((ILVariable)operand); return MakeRef(new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand)); - case ILCode.Ldnull: - return new Ast.NullReferenceExpression(); - case ILCode.Ldstr: - return new Ast.PrimitiveExpression(operand); + case ILCode.Ldnull: return new Ast.NullReferenceExpression(); + case ILCode.Ldstr: return new Ast.PrimitiveExpression(operand); case ILCode.Ldtoken: if (operand is Cecil.TypeReference) { return new Ast.TypeOfExpression { Type = operandAsTypeRef }.Member("TypeHandle"); } else { return InlineAssembly(byteCode, args); } - case ILCode.Leave: return new GotoStatement() { Label = ((ILLabel)operand).Name }; - case ILCode.Localloc: return InlineAssembly(byteCode, args); - case ILCode.Mkrefany: return InlineAssembly(byteCode, args); - case ILCode.Newobj: - { - Cecil.TypeReference declaringType = ((MethodReference)operand).DeclaringType; - - if (declaringType is ArrayType) { - ComposedType ct = AstBuilder.ConvertType((ArrayType)declaringType) as ComposedType; - if (ct != null && ct.ArraySpecifiers.Count >= 1) { - var ace = new Ast.ArrayCreateExpression(); - ct.ArraySpecifiers.First().Remove(); - ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); - ace.Type = ct; - ace.Arguments.AddRange(args); - return ace; - } - } - var oce = new Ast.ObjectCreateExpression(); - oce.Type = AstBuilder.ConvertType(declaringType); - oce.Arguments.AddRange(args); - return oce.WithAnnotation(operand); - } - case ILCode.No: return InlineAssembly(byteCode, args); - case ILCode.Nop: return null; - case ILCode.Pop: return arg1; - case ILCode.Readonly: return InlineAssembly(byteCode, args); - case ILCode.Refanytype: return InlineAssembly(byteCode, args); - case ILCode.Refanyval: return InlineAssembly(byteCode, args); - case ILCode.Ret: { - if (methodDef.ReturnType.FullName != "System.Void") { - return new Ast.ReturnStatement { Expression = arg1 }; - } else { - return new Ast.ReturnStatement(); + case ILCode.Leave: return new GotoStatement() { Label = ((ILLabel)operand).Name }; + case ILCode.Localloc: return InlineAssembly(byteCode, args); + case ILCode.Mkrefany: return InlineAssembly(byteCode, args); + case ILCode.Newobj: { + Cecil.TypeReference declaringType = ((MethodReference)operand).DeclaringType; + if (declaringType is ArrayType) { + ComposedType ct = AstBuilder.ConvertType((ArrayType)declaringType) as ComposedType; + if (ct != null && ct.ArraySpecifiers.Count >= 1) { + var ace = new Ast.ArrayCreateExpression(); + ct.ArraySpecifiers.First().Remove(); + ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); + ace.Type = ct; + ace.Arguments.AddRange(args); + return ace; } } - case ILCode.Rethrow: return new Ast.ThrowStatement(); - case ILCode.Sizeof: - return new Ast.SizeOfExpression { Type = operandAsTypeRef }; - case ILCode.Starg: - return new Ast.AssignmentExpression(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand), arg1); - case ILCode.Stloc: { - ILVariable locVar = (ILVariable)operand; - localVariablesToDefine.Add(locVar); - return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name).WithAnnotation(locVar), arg1); + var oce = new Ast.ObjectCreateExpression(); + oce.Type = AstBuilder.ConvertType(declaringType); + oce.Arguments.AddRange(args); + return oce.WithAnnotation(operand); + } + case ILCode.No: return InlineAssembly(byteCode, args); + case ILCode.Nop: return null; + case ILCode.Pop: return arg1; + case ILCode.Readonly: return InlineAssembly(byteCode, args); + case ILCode.Refanytype: return InlineAssembly(byteCode, args); + case ILCode.Refanyval: return InlineAssembly(byteCode, args); + case ILCode.Ret: + if (methodDef.ReturnType.FullName != "System.Void") { + return new Ast.ReturnStatement { Expression = arg1 }; + } else { + return new Ast.ReturnStatement(); } - case ILCode.Switch: return InlineAssembly(byteCode, args); - case ILCode.Tail: return InlineAssembly(byteCode, args); - case ILCode.Throw: return new Ast.ThrowStatement { Expression = arg1 }; - case ILCode.Unaligned: return InlineAssembly(byteCode, args); - case ILCode.Volatile: return InlineAssembly(byteCode, args); - default: throw new Exception("Unknown OpCode: " + byteCode.Code); + case ILCode.Rethrow: return new Ast.ThrowStatement(); + case ILCode.Sizeof: return new Ast.SizeOfExpression { Type = operandAsTypeRef }; + case ILCode.Starg: return new Ast.AssignmentExpression(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand), arg1); + case ILCode.Stloc: { + ILVariable locVar = (ILVariable)operand; + localVariablesToDefine.Add(locVar); + return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name).WithAnnotation(locVar), arg1); + } + case ILCode.Switch: return InlineAssembly(byteCode, args); + case ILCode.Tail: return InlineAssembly(byteCode, args); + case ILCode.Throw: return new Ast.ThrowStatement { Expression = arg1 }; + case ILCode.Unaligned: return InlineAssembly(byteCode, args); + case ILCode.Volatile: return InlineAssembly(byteCode, args); + default: throw new Exception("Unknown OpCode: " + byteCode.Code); } } @@ -707,6 +642,31 @@ static Expression InlineAssembly(ILExpression byteCode, List arg return new IdentifierExpression(byteCode.Code.GetName()).Invoke(args); } + static string FormatByteCodeOperand(object operand) + { + if (operand == null) { + return string.Empty; + //} else if (operand is ILExpression) { + // return string.Format("IL_{0:X2}", ((ILExpression)operand).Offset); + } else if (operand is MethodReference) { + return ((MethodReference)operand).Name + "()"; + } else if (operand is Cecil.TypeReference) { + return ((Cecil.TypeReference)operand).FullName; + } else if (operand is VariableDefinition) { + return ((VariableDefinition)operand).Name; + } else if (operand is ParameterDefinition) { + return ((ParameterDefinition)operand).Name; + } else if (operand is FieldReference) { + return ((FieldReference)operand).Name; + } else if (operand is string) { + return "\"" + operand + "\""; + } else if (operand is int) { + return operand.ToString(); + } else { + return operand.ToString(); + } + } + static IEnumerable ConvertTypeArguments(MethodReference cecilMethod) { GenericInstanceMethod g = cecilMethod as GenericInstanceMethod; From 3c4b3bf2e9d5dfe90e33949ad1f4134bae71b91c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 6 Mar 2011 12:15:03 +0000 Subject: [PATCH 06/10] Use enum as case value if possible. --- ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 75c6ad1cd3..4847e10fae 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -123,7 +123,7 @@ IEnumerable TransformNode(ILNode node) SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition) }; foreach (var caseBlock in ilSwitch.CaseBlocks) { SwitchSection section = new SwitchSection(); - section.CaseLabels.AddRange(caseBlock.Values.Select(i => new CaseLabel() { Expression = new PrimitiveExpression(i) })); + section.CaseLabels.AddRange(caseBlock.Values.Select(i => new CaseLabel() { Expression = AstBuilder.MakePrimitive(i, ilSwitch.Condition.InferredType) })); section.Statements.Add(TransformBlock(caseBlock)); switchStmt.SwitchSections.Add(section); } From 84d45645bd696a80280e940f014c6ba16f4a2a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 6 Mar 2011 13:26:53 +0000 Subject: [PATCH 07/10] Generate switch default case. Closes #26 --- .../Ast/AstMethodBodyBuilder.cs | 6 +++- ICSharpCode.Decompiler/ILAst/GotoRemoval.cs | 9 +++++- .../ILAst/ILAstOptimizer.cs | 28 ++++++++++++++++--- ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 2 +- .../CSharp/OutputVisitor/OutputVisitor.cs | 18 ++++++++---- 5 files changed, 51 insertions(+), 12 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 4847e10fae..5c047e0f42 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -123,7 +123,11 @@ IEnumerable TransformNode(ILNode node) SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition) }; foreach (var caseBlock in ilSwitch.CaseBlocks) { SwitchSection section = new SwitchSection(); - section.CaseLabels.AddRange(caseBlock.Values.Select(i => new CaseLabel() { Expression = AstBuilder.MakePrimitive(i, ilSwitch.Condition.InferredType) })); + if (caseBlock.Values != null) { + section.CaseLabels.AddRange(caseBlock.Values.Select(i => new CaseLabel() { Expression = AstBuilder.MakePrimitive(i, ilSwitch.Condition.InferredType) })); + } else { + section.CaseLabels.Add(new CaseLabel()); + } section.Statements.Add(TransformBlock(caseBlock)); switchStmt.SwitchSections.Add(section); } diff --git a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs index 6db1374d22..302bdeb321 100644 --- a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs +++ b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs @@ -60,6 +60,8 @@ public static void RemoveRedundantCode(ILBlock method) // Remove redundant case blocks altogether foreach(ILSwitch ilSwitch in method.GetSelfAndChildrenRecursive()) { foreach(ILBlock ilCase in ilSwitch.CaseBlocks) { + Debug.Assert(ilCase.EntryGoto == null); + int count = ilCase.Body.Count; if (count >= 2) { if (!ilCase.Body[count - 2].CanFallthough() && @@ -68,7 +70,12 @@ public static void RemoveRedundantCode(ILBlock method) } } } - ilSwitch.CaseBlocks.RemoveAll(b => b.Body.Count == 1 && b.Body.Single().Match(ILCode.LoopOrSwitchBreak)); + + var defaultCase = ilSwitch.CaseBlocks.Where(cb => cb.Values == null).SingleOrDefault(); + // If there is no default block, remove empty case blocks + if (defaultCase == null || (defaultCase.Body.Count == 1 && defaultCase.Body.Single().Match(ILCode.LoopOrSwitchBreak))) { + ilSwitch.CaseBlocks.RemoveAll(b => b.Body.Count == 1 && b.Body.Single().Match(ILCode.LoopOrSwitchBreak)); + } } // Remove redundant return diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index ff08893d03..2775f3551c 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -584,18 +584,20 @@ List FindConditions(HashSet scope, ControlFlowNode entr if (condBranch.Match(ILCode.Switch, out caseLabels)) { ILSwitch ilSwitch = new ILSwitch() { Condition = condBranch.Arguments.Single() }; - result.Add(new ILBasicBlock() { + ILBasicBlock newBB = new ILBasicBlock() { EntryLabel = block.EntryLabel, // Keep the entry label Body = { ilSwitch }, FallthoughGoto = block.FallthoughGoto - }); + }; + result.Add(newBB); // Remove the item so that it is not picked up as content scope.RemoveOrThrow(node); // Pull in code of cases + ILLabel fallLabel = (ILLabel)block.FallthoughGoto.Operand; ControlFlowNode fallTarget = null; - labelToCfNode.TryGetValue((ILLabel)block.FallthoughGoto.Operand, out fallTarget); + labelToCfNode.TryGetValue(fallLabel, out fallTarget); HashSet frontiers = new HashSet(); if (fallTarget != null) @@ -614,7 +616,10 @@ List FindConditions(HashSet scope, ControlFlowNode entr // Find or create new case block ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.Where(b => b.EntryGoto.Operand == condLabel).FirstOrDefault(); if (caseBlock == null) { - caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, condLabel) }; + caseBlock = new ILSwitch.CaseBlock() { + Values = new List(), + EntryGoto = new ILExpression(ILCode.Br, condLabel) + }; ilSwitch.CaseBlocks.Add(caseBlock); ControlFlowNode condTarget = null; @@ -629,6 +634,21 @@ List FindConditions(HashSet scope, ControlFlowNode entr } caseBlock.Values.Add(i); } + + // Heuristis to determine if we want to use fallthough as default case + if (fallTarget != null && !frontiers.Contains(fallTarget)) { + HashSet content = FindDominatedNodes(scope, fallTarget); + if (content.Any()) { + var caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, fallLabel) }; + ilSwitch.CaseBlocks.Add(caseBlock); + newBB.FallthoughGoto = null; + + scope.ExceptWith(content); + caseBlock.Body.AddRange(FindConditions(content, fallTarget)); + // 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) } }); + } + } } // Two-way branch diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index 8d4832fe83..6eb02de941 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -444,7 +444,7 @@ public class ILSwitch: ILNode { public class CaseBlock: ILBlock { - public List Values = new List(); + public List Values; // null for the default case public override void WriteTo(ITextOutput output) { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs index 5f9d0f1c79..da02c47d5f 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs @@ -1529,8 +1529,13 @@ public object VisitSwitchStatement(SwitchStatement switchStatement, object data) public object VisitSwitchSection(SwitchSection switchSection, object data) { StartNode(switchSection); - foreach (var label in switchSection.CaseLabels) + bool first = true; + foreach (var label in switchSection.CaseLabels) { + if (!first) + NewLine(); label.AcceptVisitor(this, data); + first = false; + } foreach (var statement in switchSection.Statements) statement.AcceptVisitor(this, data); return EndNode(switchSection); @@ -1539,11 +1544,14 @@ public object VisitSwitchSection(SwitchSection switchSection, object data) public object VisitCaseLabel(CaseLabel caseLabel, object data) { StartNode(caseLabel); - WriteKeyword("case"); - Space(); - caseLabel.Expression.AcceptVisitor(this, data); + if (caseLabel.Expression.IsNull) { + WriteKeyword("default"); + } else { + WriteKeyword("case"); + Space(); + caseLabel.Expression.AcceptVisitor(this, data); + } WriteToken(":", CaseLabel.Roles.Colon); - NewLine(); return EndNode(caseLabel); } From 456758e5428ac7013f4c32566eda295ccf5f9c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 6 Mar 2011 14:04:49 +0000 Subject: [PATCH 08/10] Apply the offset in switch expression --- .../ILAst/ILAstOptimizer.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 2775f3551c..f91a2d43d0 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -594,6 +594,13 @@ List FindConditions(HashSet scope, ControlFlowNode entr // Remove the item so that it is not picked up as content scope.RemoveOrThrow(node); + // Find the switch offset + int addValue = 0; + List subArgs; + if (ilSwitch.Condition.Match(ILCode.Sub, out subArgs) && subArgs[1].Match(ILCode.Ldc_I4, out addValue)) { + ilSwitch.Condition = subArgs[0]; + } + // Pull in code of cases ILLabel fallLabel = (ILLabel)block.FallthoughGoto.Operand; ControlFlowNode fallTarget = null; @@ -632,7 +639,7 @@ List FindConditions(HashSet scope, ControlFlowNode entr caseBlock.Body.Add(new ILBasicBlock() { Body = { new ILExpression(ILCode.LoopOrSwitchBreak, null) } }); } } - caseBlock.Values.Add(i); + caseBlock.Values.Add(i + addValue); } // Heuristis to determine if we want to use fallthough as default case @@ -862,6 +869,17 @@ public static bool Match(this ILNode node, ILCode code, out T operand) return false; } + public static bool Match(this ILNode node, ILCode code, out List args) + { + ILExpression expr = node as ILExpression; + if (expr != null && expr.Prefixes == null && expr.Code == code) { + args = expr.Arguments; + return true; + } + args = null; + return false; + } + public static bool MatchBrTure(this ILBasicBlock bb, out ILExpression condition, out ILLabel trueLabel, out ILLabel falseLabel) { if (bb.Body.Count == 1) { From 4c38e164dee3efc33b7789a99046c0e9633390a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 6 Mar 2011 14:40:29 +0000 Subject: [PATCH 09/10] Minor refactoring of ILAst pattern matching --- .../ILAst/ILAstOptimizer.cs | 112 +++++++++--------- 1 file changed, 58 insertions(+), 54 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index f91a2d43d0..6e77e3a9c3 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -236,40 +236,26 @@ void PeepholeOptimizations(ILBlock block) } while(modified); } - bool IsStloc(ILBasicBlock bb, ref ILVariable locVar, ref ILExpression val, ref ILLabel fallLabel) - { - if (bb.Body.Count == 1) { - ILExpression expr; - if (bb.Body[0].Match(ILCode.Stloc, out expr)) { - locVar = (ILVariable)expr.Operand; - val = expr.Arguments[0]; - fallLabel = (ILLabel)bb.FallthoughGoto.Operand; - return true; - } - } - return false; - } - // scope is modified if successful bool TrySimplifyTernaryOperator(List scope, ILBasicBlock head) { Debug.Assert(scope.Contains(head)); - ILExpression condExpr = null; - ILLabel trueLabel = null; - ILLabel falseLabel = null; - ILVariable trueLocVar = null; - ILExpression trueExpr = null; - ILLabel trueFall = null; - ILVariable falseLocVar = null; - ILExpression falseExpr = null; - ILLabel falseFall = null; - - if(head.MatchBrTure(out condExpr, out trueLabel, out falseLabel) && + ILExpression condExpr; + ILLabel trueLabel; + ILLabel falseLabel; + ILVariable trueLocVar; + ILExpression trueExpr; + ILLabel trueFall; + ILVariable falseLocVar; + ILExpression falseExpr; + ILLabel falseFall; + + if(head.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) && labelGlobalRefCount[trueLabel] == 1 && labelGlobalRefCount[falseLabel] == 1 && - IsStloc(labelToBasicBlock[trueLabel], ref trueLocVar, ref trueExpr, ref trueFall) && - IsStloc(labelToBasicBlock[falseLabel], ref falseLocVar, ref falseExpr, ref falseFall) && + labelToBasicBlock[trueLabel].Match(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) && + labelToBasicBlock[falseLabel].Match(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall) && trueLocVar == falseLocVar && trueFall == falseFall) { @@ -298,7 +284,7 @@ bool TrySimplifyShortCircuit(List scope, ILBasicBlock head) ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; - if(head.MatchBrTure(out condExpr, out trueLabel, out falseLabel)) { + if(head.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { for (int pass = 0; pass < 2; pass++) { // On the second pass, swap labels and negate expression of the first branch @@ -314,7 +300,7 @@ bool TrySimplifyShortCircuit(List scope, ILBasicBlock head) if (scope.Contains(nextBasicBlock) && nextBasicBlock != head && labelGlobalRefCount[nextBasicBlock.EntryLabel] == 1 && - nextBasicBlock.MatchBrTure(out nextCondExpr, out nextTrueLablel, out nextFalseLabel) && + nextBasicBlock.Match(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) && (otherLablel == nextFalseLabel || otherLablel == nextTrueLablel)) { // Create short cicuit branch @@ -355,9 +341,7 @@ void DuplicateReturnStatements(ILBlock method) foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { 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)) - { + 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]; @@ -365,17 +349,17 @@ void DuplicateReturnStatements(ILBlock method) // Inline return statement ILNode target; - ILExpression retExpr; + List retArgs; if (nextSibling.TryGetValue(targetLabel, out target) && - target.Match(ILCode.Ret, out retExpr)) + target.Match(ILCode.Ret, out retArgs)) { ILVariable locVar; object constValue; - if (retExpr.Arguments.Count == 0) { + if (retArgs.Count == 0) { block.Body[i] = new ILExpression(ILCode.Ret, null); - } else if (retExpr.Arguments.Single().Match(ILCode.Ldloc, out locVar)) { + } else if (retArgs.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)) { + } else if (retArgs.Single().Match(ILCode.Ldc_I4, out constValue)) { block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldc_I4, constValue)); } } @@ -467,7 +451,7 @@ List FindLoops(HashSet scope, ControlFlowNode entryPoin ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; - if(basicBlock.MatchBrTure(out condExpr, out trueLabel, out falseLabel)) + if(basicBlock.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { ControlFlowNode trueTarget; labelToCfNode.TryGetValue(trueLabel, out trueTarget); @@ -581,9 +565,10 @@ List FindConditions(HashSet scope, ControlFlowNode entr // Switch ILLabel[] caseLabels; - if (condBranch.Match(ILCode.Switch, out caseLabels)) { + List switchArgs; + if (condBranch.Match(ILCode.Switch, out caseLabels, out switchArgs)) { - ILSwitch ilSwitch = new ILSwitch() { Condition = condBranch.Arguments.Single() }; + ILSwitch ilSwitch = new ILSwitch() { Condition = switchArgs.Single() }; ILBasicBlock newBB = new ILBasicBlock() { EntryLabel = block.EntryLabel, // Keep the entry label Body = { ilSwitch }, @@ -662,7 +647,7 @@ List FindConditions(HashSet scope, ControlFlowNode entr ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; - if(block.MatchBrTure(out condExpr, out trueLabel, out falseLabel)) { + if(block.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { // Swap bodies since that seems to be the usual C# order ILLabel temp = trueLabel; @@ -852,17 +837,12 @@ public static bool Match(this ILNode node, ILCode code) 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; - } - public static bool Match(this ILNode node, ILCode code, out T operand) { ILExpression expr = node as ILExpression; if (expr != null && expr.Prefixes == null && expr.Code == code) { operand = (T)expr.Operand; + Debug.Assert(expr.Arguments.Count == 0); return true; } operand = default(T); @@ -873,6 +853,7 @@ public static bool Match(this ILNode node, ILCode code, out List a { ILExpression expr = node as ILExpression; if (expr != null && expr.Prefixes == null && expr.Code == code) { + Debug.Assert(expr.Operand == null); args = expr.Arguments; return true; } @@ -880,18 +861,41 @@ public static bool Match(this ILNode node, ILCode code, out List a return false; } - public static bool MatchBrTure(this ILBasicBlock bb, out ILExpression condition, out ILLabel trueLabel, out ILLabel falseLabel) + public static bool Match(this ILNode node, ILCode code, out T operand, out List args) + { + ILExpression expr = node as ILExpression; + if (expr != null && expr.Prefixes == null && expr.Code == code) { + operand = (T)expr.Operand; + args = expr.Arguments; + return true; + } + operand = default(T); + args = null; + return false; + } + + public static bool Match(this ILNode node, ILCode code, out T operand, out ILExpression arg) + { + List args; + if (node.Match(code, out operand, out args)) { + arg = args.Single(); + return true; + } + arg = null; + return false; + } + + public static bool Match(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel fallLabel) { if (bb.Body.Count == 1) { - if (bb.Body[0].Match(ILCode.Brtrue, out trueLabel)) { - condition = ((ILExpression)bb.Body[0]).Arguments.Single(); - falseLabel = (ILLabel)((ILExpression)bb.FallthoughGoto).Operand; + if (bb.Body[0].Match(code, out operand, out arg)) { + fallLabel = (ILLabel)bb.FallthoughGoto.Operand; return true; } } - condition = null; - trueLabel = null; - falseLabel = null; + operand = default(T); + arg = null; + fallLabel = null; return false; } From de6d657f3f2c2279f00f15d055911a20f02baa8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 6 Mar 2011 15:07:44 +0000 Subject: [PATCH 10/10] Fixed return duplication in the case when the last return statement was already removed. --- ICSharpCode.Decompiler/ILAst/GotoRemoval.cs | 2 +- .../ILAst/ILAstOptimizer.cs | 41 +++++++++---------- ICSharpCode.Decompiler/ILAst/ILCodes.cs | 2 + 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs index 302bdeb321..15e9984aa0 100644 --- a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs +++ b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs @@ -64,7 +64,7 @@ public static void RemoveRedundantCode(ILBlock method) int count = ilCase.Body.Count; if (count >= 2) { - if (!ilCase.Body[count - 2].CanFallthough() && + if (!ilCase.Body[count - 2].CanFallThough() && ilCase.Body[count - 1].Match(ILCode.LoopOrSwitchBreak)) { ilCase.Body.RemoveAt(count - 1); } diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 6e77e3a9c3..6aa3f352cc 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -350,17 +350,22 @@ void DuplicateReturnStatements(ILBlock method) // Inline return statement ILNode target; List retArgs; - if (nextSibling.TryGetValue(targetLabel, out target) && - target.Match(ILCode.Ret, out retArgs)) - { - ILVariable locVar; - object constValue; - if (retArgs.Count == 0) { + if (nextSibling.TryGetValue(targetLabel, out target)) { + if (target.Match(ILCode.Ret, out retArgs)) { + ILVariable locVar; + object constValue; + if (retArgs.Count == 0) { + block.Body[i] = new ILExpression(ILCode.Ret, null); + } else if (retArgs.Single().Match(ILCode.Ldloc, out locVar)) { + block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldloc, locVar)); + } else if (retArgs.Single().Match(ILCode.Ldc_I4, out constValue)) { + block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldc_I4, constValue)); + } + } + } else { + if (method.Body.Count > 0 && method.Body.Last() == targetLabel) { + // It exits the main method - so it is same as return; block.Body[i] = new ILExpression(ILCode.Ret, null); - } else if (retArgs.Single().Match(ILCode.Ldloc, out locVar)) { - block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldloc, locVar)); - } else if (retArgs.Single().Match(ILCode.Ldc_I4, out constValue)) { - block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldc_I4, constValue)); } } } @@ -796,8 +801,8 @@ void FlattenIfStatements(ILNode node) for (int i = 0; i < block.Body.Count; i++) { ILCondition cond = block.Body[i] as ILCondition; if (cond != null) { - bool trueExits = cond.TrueBlock.Body.Count > 0 && !cond.TrueBlock.Body.Last().CanFallthough(); - bool falseExits = cond.FalseBlock.Body.Count > 0 && !cond.FalseBlock.Body.Last().CanFallthough(); + bool trueExits = cond.TrueBlock.Body.Count > 0 && !cond.TrueBlock.Body.Last().CanFallThough(); + bool falseExits = cond.FalseBlock.Body.Count > 0 && !cond.FalseBlock.Body.Last().CanFallThough(); if (trueExits) { // Move the false block after the condition @@ -899,19 +904,11 @@ public static bool Match(this ILBasicBlock bb, ILCode code, out T operand, ou return false; } - public static bool CanFallthough(this ILNode node) + public static bool CanFallThough(this ILNode node) { ILExpression expr = node as ILExpression; if (expr != null) { - switch(expr.Code) { - case ILCode.Br: - case ILCode.Ret: - case ILCode.Throw: - case ILCode.Rethrow: - case ILCode.LoopContinue: - case ILCode.LoopOrSwitchBreak: - return false; - } + return expr.Code.CanFallThough(); } return true; } diff --git a/ICSharpCode.Decompiler/ILAst/ILCodes.cs b/ICSharpCode.Decompiler/ILAst/ILCodes.cs index 43acc361ec..afb79315c8 100644 --- a/ICSharpCode.Decompiler/ILAst/ILCodes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILCodes.cs @@ -286,6 +286,8 @@ public static bool CanFallThough(this ILCode code) case ILCode.Endfinally: case ILCode.Throw: case ILCode.Rethrow: + case ILCode.LoopContinue: + case ILCode.LoopOrSwitchBreak: return false; default: return true;