From 51eb00aac724dda67eaf779a86babcc917395b3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Mon, 23 May 2011 00:14:59 +0100 Subject: [PATCH] Remove unreachable code in ILAstBuilder. Closes #134. Closes #151. Closes #171. --- ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs | 44 ++++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index aed211f56..f6edfad85 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -426,13 +426,12 @@ namespace ICSharpCode.Decompiler.ILAst } } - // Occasionally the compiler generates unreachable code - it can be usually just ignored - var reachableBody = body.Where(b => b.StackBefore != null); - var unreachableBody = body.Where(b => b.StackBefore == null); + // Occasionally the compilers or obfuscators generate unreachable code (which migt be intentonally invalid) + // I belive it is safe to just remove it + body.RemoveAll(b => b.StackBefore == null); // Genertate temporary variables to replace stack - // Unrachable code does not need temporary variables - the values are never pushed on the stack for consuption - foreach(ByteCode byteCode in reachableBody) { + foreach(ByteCode byteCode in body) { int argIdx = 0; int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count; for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) { @@ -450,19 +449,18 @@ namespace ICSharpCode.Decompiler.ILAst // Try to use single temporary variable insted of several if possilbe (especially useful for dup) // This has to be done after all temporary variables are assigned so we know about all loads - // Unrachable code will not have any StoreTo - foreach(ByteCode byteCode in reachableBody) { + foreach(ByteCode byteCode in body) { if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1) { var locVars = byteCode.StoreTo; // For each of the variables, find the location where it is loaded - there should be preciesly one - var loadedBy = locVars.Select(locVar => reachableBody.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList(); + var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList(); // We now know that all the variables have a single load, // Let's make sure that they have also a single store - us if (loadedBy.All(slot => slot.PushedBy.Length == 1 && slot.PushedBy[0] == byteCode)) { // Great - we can reduce everything into single variable ILVariable tmpVar = new ILVariable() { Name = string.Format("expr_{0:X2}", byteCode.Offset), IsGenerated = true }; byteCode.StoreTo = new List() { tmpVar }; - foreach(ByteCode bc in reachableBody) { + foreach(ByteCode bc in body) { for (int i = 0; i < bc.StackBefore.Count; i++) { // Is it one of the variable to be merged? if (locVars.Contains(bc.StackBefore[i].LoadFrom)) { @@ -658,12 +656,14 @@ namespace ICSharpCode.Decompiler.ILAst // Find the first and widest scope int tryStart = ehs.Min(eh => eh.TryStart.Offset); int tryEnd = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.Offset); - var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).ToList(); + var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).OrderBy(eh => eh.TryStart.Offset).ToList(); + + // Remember that any part of the body migt have been removed due to unreachability // Cut all instructions up to the try block { - int tryStartIdx; - for (tryStartIdx = 0; body[tryStartIdx].Offset != tryStart; tryStartIdx++); + int tryStartIdx = 0; + while (tryStartIdx < body.Count && body[tryStartIdx].Offset < tryStart) tryStartIdx++; ast.AddRange(ConvertToAst(body.CutRange(0, tryStartIdx))); } @@ -671,24 +671,22 @@ namespace ICSharpCode.Decompiler.ILAst { HashSet nestedEHs = new HashSet(ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd))); ehs.ExceptWith(nestedEHs); - int tryEndIdx; - for (tryEndIdx = 0; tryEndIdx < body.Count && body[tryEndIdx].Offset != tryEnd; tryEndIdx++); + int tryEndIdx = 0; + while (tryEndIdx < body.Count && body[tryEndIdx].Offset < tryEnd) tryEndIdx++; tryCatchBlock.TryBlock = new ILBlock(ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs)); } // Cut all handlers tryCatchBlock.CatchBlocks = new List(); foreach(ExceptionHandler eh in handlers) { - int startIndex; - for (startIndex = 0; body[startIndex].Offset != eh.HandlerStart.Offset; startIndex++); - int endInclusiveIndex; - if (eh.HandlerEnd == null) endInclusiveIndex = body.Count - 1; - // Note that the end(exclusive) instruction may not necessarly be in our body - else for (endInclusiveIndex = 0; body[endInclusiveIndex].Next.Offset != eh.HandlerEnd.Offset; endInclusiveIndex++); - int count = 1 + endInclusiveIndex - startIndex; - HashSet nestedEHs = new HashSet(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < eh.HandlerEnd.Offset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= eh.HandlerEnd.Offset))); + int handlerEndOffset = eh.HandlerEnd == null ? methodDef.Body.CodeSize : eh.HandlerEnd.Offset; + int startIdx = 0; + while (startIdx < body.Count && body[startIdx].Offset < eh.HandlerStart.Offset) startIdx++; + int endIdx = 0; + while (endIdx < body.Count && body[endIdx].Offset < handlerEndOffset) endIdx++; + HashSet nestedEHs = new HashSet(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < handlerEndOffset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= handlerEndOffset))); ehs.ExceptWith(nestedEHs); - List handlerAst = ConvertToAst(body.CutRange(startIndex, count), nestedEHs); + List handlerAst = ConvertToAst(body.CutRange(startIdx, endIdx - startIdx), nestedEHs); if (eh.HandlerType == ExceptionHandlerType.Catch) { ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock() { ExceptionType = eh.CatchType,