Browse Source

Remove unreachable code in ILAstBuilder. Closes #134. Closes #151. Closes #171.

pull/143/merge
David Srbecký 15 years ago
parent
commit
51eb00aac7
  1. 44
      ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs

44
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 // Occasionally the compilers or obfuscators generate unreachable code (which migt be intentonally invalid)
var reachableBody = body.Where(b => b.StackBefore != null); // I belive it is safe to just remove it
var unreachableBody = body.Where(b => b.StackBefore == null); body.RemoveAll(b => b.StackBefore == null);
// Genertate temporary variables to replace stack // 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 body) {
foreach(ByteCode byteCode in reachableBody) {
int argIdx = 0; int argIdx = 0;
int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count; int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count;
for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) { 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) // 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 // 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 body) {
foreach(ByteCode byteCode in reachableBody) {
if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1) { if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1) {
var locVars = byteCode.StoreTo; var locVars = byteCode.StoreTo;
// For each of the variables, find the location where it is loaded - there should be preciesly one // 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, // We now know that all the variables have a single load,
// Let's make sure that they have also a single store - us // Let's make sure that they have also a single store - us
if (loadedBy.All(slot => slot.PushedBy.Length == 1 && slot.PushedBy[0] == byteCode)) { if (loadedBy.All(slot => slot.PushedBy.Length == 1 && slot.PushedBy[0] == byteCode)) {
// Great - we can reduce everything into single variable // Great - we can reduce everything into single variable
ILVariable tmpVar = new ILVariable() { Name = string.Format("expr_{0:X2}", byteCode.Offset), IsGenerated = true }; ILVariable tmpVar = new ILVariable() { Name = string.Format("expr_{0:X2}", byteCode.Offset), IsGenerated = true };
byteCode.StoreTo = new List<ILVariable>() { tmpVar }; byteCode.StoreTo = new List<ILVariable>() { tmpVar };
foreach(ByteCode bc in reachableBody) { foreach(ByteCode bc in body) {
for (int i = 0; i < bc.StackBefore.Count; i++) { for (int i = 0; i < bc.StackBefore.Count; i++) {
// Is it one of the variable to be merged? // Is it one of the variable to be merged?
if (locVars.Contains(bc.StackBefore[i].LoadFrom)) { if (locVars.Contains(bc.StackBefore[i].LoadFrom)) {
@ -658,12 +656,14 @@ namespace ICSharpCode.Decompiler.ILAst
// Find the first and widest scope // Find the first and widest scope
int tryStart = ehs.Min(eh => eh.TryStart.Offset); int tryStart = ehs.Min(eh => eh.TryStart.Offset);
int tryEnd = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.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 // Cut all instructions up to the try block
{ {
int tryStartIdx; int tryStartIdx = 0;
for (tryStartIdx = 0; body[tryStartIdx].Offset != tryStart; tryStartIdx++); while (tryStartIdx < body.Count && body[tryStartIdx].Offset < tryStart) tryStartIdx++;
ast.AddRange(ConvertToAst(body.CutRange(0, tryStartIdx))); ast.AddRange(ConvertToAst(body.CutRange(0, tryStartIdx)));
} }
@ -671,24 +671,22 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
HashSet<ExceptionHandler> nestedEHs = new HashSet<ExceptionHandler>(ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd))); HashSet<ExceptionHandler> nestedEHs = new HashSet<ExceptionHandler>(ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd)));
ehs.ExceptWith(nestedEHs); ehs.ExceptWith(nestedEHs);
int tryEndIdx; int tryEndIdx = 0;
for (tryEndIdx = 0; tryEndIdx < body.Count && body[tryEndIdx].Offset != tryEnd; tryEndIdx++); while (tryEndIdx < body.Count && body[tryEndIdx].Offset < tryEnd) tryEndIdx++;
tryCatchBlock.TryBlock = new ILBlock(ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs)); tryCatchBlock.TryBlock = new ILBlock(ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs));
} }
// Cut all handlers // Cut all handlers
tryCatchBlock.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>(); tryCatchBlock.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>();
foreach(ExceptionHandler eh in handlers) { foreach(ExceptionHandler eh in handlers) {
int startIndex; int handlerEndOffset = eh.HandlerEnd == null ? methodDef.Body.CodeSize : eh.HandlerEnd.Offset;
for (startIndex = 0; body[startIndex].Offset != eh.HandlerStart.Offset; startIndex++); int startIdx = 0;
int endInclusiveIndex; while (startIdx < body.Count && body[startIdx].Offset < eh.HandlerStart.Offset) startIdx++;
if (eh.HandlerEnd == null) endInclusiveIndex = body.Count - 1; int endIdx = 0;
// Note that the end(exclusive) instruction may not necessarly be in our body while (endIdx < body.Count && body[endIdx].Offset < handlerEndOffset) endIdx++;
else for (endInclusiveIndex = 0; body[endInclusiveIndex].Next.Offset != eh.HandlerEnd.Offset; endInclusiveIndex++); HashSet<ExceptionHandler> nestedEHs = new HashSet<ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < handlerEndOffset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= handlerEndOffset)));
int count = 1 + endInclusiveIndex - startIndex;
HashSet<ExceptionHandler> nestedEHs = new HashSet<ExceptionHandler>(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)));
ehs.ExceptWith(nestedEHs); ehs.ExceptWith(nestedEHs);
List<ILNode> handlerAst = ConvertToAst(body.CutRange(startIndex, count), nestedEHs); List<ILNode> handlerAst = ConvertToAst(body.CutRange(startIdx, endIdx - startIdx), nestedEHs);
if (eh.HandlerType == ExceptionHandlerType.Catch) { if (eh.HandlerType == ExceptionHandlerType.Catch) {
ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock() { ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock() {
ExceptionType = eh.CatchType, ExceptionType = eh.CatchType,

Loading…
Cancel
Save