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 @@ -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 @@ -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<ILVariable>() { 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 @@ -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 @@ -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)));
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<ILTryCatchBlock.CatchBlock>();
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<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)));
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<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)));
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) {
ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock() {
ExceptionType = eh.CatchType,

Loading…
Cancel
Save