|
|
|
@ -10,54 +10,59 @@ namespace ICSharpCode.Decompiler.ILAst
@@ -10,54 +10,59 @@ namespace ICSharpCode.Decompiler.ILAst
|
|
|
|
|
{ |
|
|
|
|
public class ILAstBuilder |
|
|
|
|
{ |
|
|
|
|
static ByteCode[] EmptyByteCodeArray = new ByteCode[] {}; |
|
|
|
|
|
|
|
|
|
/// <summary> Immutable </summary>
|
|
|
|
|
class StackSlot |
|
|
|
|
{ |
|
|
|
|
public List<ByteCode> PushedBy; // One of those
|
|
|
|
|
public ILVariable LoadFrom; // Where can we get the value from in AST
|
|
|
|
|
public readonly ByteCode[] PushedBy; // One of those
|
|
|
|
|
public readonly ILVariable LoadFrom; // Where can we get the value from in AST
|
|
|
|
|
|
|
|
|
|
public StackSlot() |
|
|
|
|
public StackSlot(ByteCode[] pushedBy, ILVariable loadFrom) |
|
|
|
|
{ |
|
|
|
|
this.PushedBy = pushedBy; |
|
|
|
|
this.LoadFrom = loadFrom; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public StackSlot(ByteCode pushedBy) |
|
|
|
|
{ |
|
|
|
|
this.PushedBy = new List<ByteCode>(1); |
|
|
|
|
this.PushedBy.Add(pushedBy); |
|
|
|
|
this.PushedBy = new[] { pushedBy }; |
|
|
|
|
this.LoadFrom = null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static List<StackSlot> CloneStack(List<StackSlot> stack, int? popCount) |
|
|
|
|
{ |
|
|
|
|
List<StackSlot> clone = new List<StackSlot>(); |
|
|
|
|
if (popCount.HasValue) { |
|
|
|
|
if (popCount.Value > stack.Count) { |
|
|
|
|
throw new Exception("Can not pop - the stack is empty"); |
|
|
|
|
} |
|
|
|
|
for(int i = 0; i < stack.Count - popCount.Value; i++) { |
|
|
|
|
clone.Add(new StackSlot() { PushedBy = new List<ByteCode>(stack[i].PushedBy) }); |
|
|
|
|
} |
|
|
|
|
return stack.GetRange(0, stack.Count - popCount.Value); |
|
|
|
|
} else { |
|
|
|
|
return new List<StackSlot>(0); |
|
|
|
|
} |
|
|
|
|
return clone; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> Immutable </summary>
|
|
|
|
|
class VariableSlot |
|
|
|
|
{ |
|
|
|
|
public static List<ByteCode> Empty = new List<ByteCode>(); |
|
|
|
|
{ |
|
|
|
|
public readonly ByteCode[] StoredBy; // One of those
|
|
|
|
|
public readonly bool StoredByAll; // Overestimate which is useful for exceptional control flow.
|
|
|
|
|
|
|
|
|
|
public VariableSlot(ByteCode[] storedBy, bool storedByAll) |
|
|
|
|
{ |
|
|
|
|
this.StoredBy = storedBy; |
|
|
|
|
this.StoredByAll = storedByAll; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public List<ByteCode> StoredBy = Empty; // One of those
|
|
|
|
|
public bool StoredByAll; // Overestimate which is useful for exceptional control flow.
|
|
|
|
|
public VariableSlot(ByteCode storedBy) |
|
|
|
|
{ |
|
|
|
|
this.StoredBy = new[] { storedBy }; |
|
|
|
|
this.StoredByAll = false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static VariableSlot[] CloneVariableState(VariableSlot[] state) |
|
|
|
|
{ |
|
|
|
|
VariableSlot[] clone = new ILAstBuilder.VariableSlot[state.Length]; |
|
|
|
|
if (VariableSlot.Empty.Count > 0) |
|
|
|
|
throw new Exception("Constant data corrupted"); |
|
|
|
|
VariableSlot[] clone = new VariableSlot[state.Length]; |
|
|
|
|
for (int i = 0; i < clone.Length; i++) { |
|
|
|
|
VariableSlot varSlot = state[i]; |
|
|
|
|
clone[i] = new VariableSlot() { |
|
|
|
|
StoredBy = varSlot.StoredBy.Count == 0 ? VariableSlot.Empty : new List<ByteCode>(varSlot.StoredBy), |
|
|
|
|
StoredByAll = varSlot.StoredByAll |
|
|
|
|
}; |
|
|
|
|
clone[i] = state[i]; |
|
|
|
|
} |
|
|
|
|
return clone; |
|
|
|
|
} |
|
|
|
@ -66,7 +71,7 @@ namespace ICSharpCode.Decompiler.ILAst
@@ -66,7 +71,7 @@ namespace ICSharpCode.Decompiler.ILAst
|
|
|
|
|
{ |
|
|
|
|
VariableSlot[] emptyVariableState = new VariableSlot[varCount]; |
|
|
|
|
for (int i = 0; i < emptyVariableState.Length; i++) { |
|
|
|
|
emptyVariableState[i] = new VariableSlot(); |
|
|
|
|
emptyVariableState[i] = new VariableSlot(EmptyByteCodeArray, false); |
|
|
|
|
} |
|
|
|
|
return emptyVariableState; |
|
|
|
|
} |
|
|
|
@ -75,7 +80,7 @@ namespace ICSharpCode.Decompiler.ILAst
@@ -75,7 +80,7 @@ namespace ICSharpCode.Decompiler.ILAst
|
|
|
|
|
{ |
|
|
|
|
VariableSlot[] unknownVariableState = new VariableSlot[varCount]; |
|
|
|
|
for (int i = 0; i < unknownVariableState.Length; i++) { |
|
|
|
|
unknownVariableState[i] = new VariableSlot() { StoredByAll = true }; |
|
|
|
|
unknownVariableState[i] = new VariableSlot(EmptyByteCodeArray, true); |
|
|
|
|
} |
|
|
|
|
return unknownVariableState; |
|
|
|
|
} |
|
|
|
@ -93,9 +98,9 @@ namespace ICSharpCode.Decompiler.ILAst
@@ -93,9 +98,9 @@ namespace ICSharpCode.Decompiler.ILAst
|
|
|
|
|
public string Name { get { return "IL_" + this.Offset.ToString("X2"); } } |
|
|
|
|
public ByteCode Next; |
|
|
|
|
public Instruction[] Prefixes; // Non-null only if needed
|
|
|
|
|
public List<StackSlot> StackBefore; |
|
|
|
|
public List<StackSlot> StackBefore; // Unique per bytecode; not shared
|
|
|
|
|
public List<ILVariable> StoreTo; // Store result of instruction to those AST variables
|
|
|
|
|
public VariableSlot[] VariablesBefore; |
|
|
|
|
public VariableSlot[] VariablesBefore; // Unique per bytecode; not shared
|
|
|
|
|
|
|
|
|
|
public VariableDefinition OperandAsVariable { get { return (VariableDefinition)this.Operand; } } |
|
|
|
|
|
|
|
|
@ -174,7 +179,7 @@ namespace ICSharpCode.Decompiler.ILAst
@@ -174,7 +179,7 @@ namespace ICSharpCode.Decompiler.ILAst
|
|
|
|
|
if (!first) sb.Append(","); |
|
|
|
|
if (varSlot.StoredByAll) { |
|
|
|
|
sb.Append("*"); |
|
|
|
|
} else if (varSlot.StoredBy.Count == 0) { |
|
|
|
|
} else if (varSlot.StoredBy.Length == 0) { |
|
|
|
|
sb.Append("_"); |
|
|
|
|
} else { |
|
|
|
|
bool first2 = true; |
|
|
|
@ -297,8 +302,7 @@ namespace ICSharpCode.Decompiler.ILAst
@@ -297,8 +302,7 @@ namespace ICSharpCode.Decompiler.ILAst
|
|
|
|
|
VariableSlot[] newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore); |
|
|
|
|
if (byteCode.Code == ILCode.Stloc) { |
|
|
|
|
int varIndex = ((VariableReference)byteCode.Operand).Index; |
|
|
|
|
newVariableState[varIndex].StoredBy = new List<ByteCode>(1) { byteCode }; |
|
|
|
|
newVariableState[varIndex].StoredByAll = false; |
|
|
|
|
newVariableState[varIndex] = new VariableSlot(byteCode); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// After the leave, finally block might have touched the variables
|
|
|
|
@ -353,10 +357,10 @@ namespace ICSharpCode.Decompiler.ILAst
@@ -353,10 +357,10 @@ namespace ICSharpCode.Decompiler.ILAst
|
|
|
|
|
|
|
|
|
|
// Merge stacks - modify the target
|
|
|
|
|
for (int i = 0; i < newStack.Count; i++) { |
|
|
|
|
List<ByteCode> oldPushedBy = branchTarget.StackBefore[i].PushedBy; |
|
|
|
|
List<ByteCode> newPushedBy = oldPushedBy.Union(newStack[i].PushedBy).ToList(); |
|
|
|
|
if (newPushedBy.Count > oldPushedBy.Count) { |
|
|
|
|
branchTarget.StackBefore[i].PushedBy = newPushedBy; |
|
|
|
|
ByteCode[] oldPushedBy = branchTarget.StackBefore[i].PushedBy; |
|
|
|
|
ByteCode[] newPushedBy = oldPushedBy.Union(newStack[i].PushedBy); |
|
|
|
|
if (newPushedBy.Length > oldPushedBy.Length) { |
|
|
|
|
branchTarget.StackBefore[i] = new StackSlot(newPushedBy, null); |
|
|
|
|
modified = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -368,13 +372,13 @@ namespace ICSharpCode.Decompiler.ILAst
@@ -368,13 +372,13 @@ namespace ICSharpCode.Decompiler.ILAst
|
|
|
|
|
// All can not be unioned further
|
|
|
|
|
if (!oldSlot.StoredByAll) { |
|
|
|
|
if (newSlot.StoredByAll) { |
|
|
|
|
oldSlot.StoredByAll = true; |
|
|
|
|
branchTarget.VariablesBefore[i] = newSlot; |
|
|
|
|
modified = true; |
|
|
|
|
} else { |
|
|
|
|
List<ByteCode> oldStoredBy = oldSlot.StoredBy; |
|
|
|
|
List<ByteCode> newStoredBy = oldStoredBy.Union(newSlot.StoredBy).ToList(); |
|
|
|
|
if (newStoredBy.Count > oldStoredBy.Count) { |
|
|
|
|
oldSlot.StoredBy = newStoredBy; |
|
|
|
|
ByteCode[] oldStoredBy = oldSlot.StoredBy; |
|
|
|
|
ByteCode[] newStoredBy = oldStoredBy.Union(newSlot.StoredBy); |
|
|
|
|
if (newStoredBy.Length > oldStoredBy.Length) { |
|
|
|
|
branchTarget.VariablesBefore[i] = new VariableSlot(newStoredBy, false); |
|
|
|
|
modified = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -396,16 +400,15 @@ namespace ICSharpCode.Decompiler.ILAst
@@ -396,16 +400,15 @@ namespace ICSharpCode.Decompiler.ILAst
|
|
|
|
|
int argIdx = 0; |
|
|
|
|
int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count; |
|
|
|
|
for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) { |
|
|
|
|
StackSlot arg = byteCode.StackBefore[i]; |
|
|
|
|
ILVariable tmpVar = new ILVariable() { Name = string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), IsGenerated = true }; |
|
|
|
|
arg.LoadFrom = tmpVar; |
|
|
|
|
foreach(ByteCode pushedBy in arg.PushedBy) { |
|
|
|
|
byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].PushedBy, tmpVar); |
|
|
|
|
foreach(ByteCode pushedBy in byteCode.StackBefore[i].PushedBy) { |
|
|
|
|
if (pushedBy.StoreTo == null) { |
|
|
|
|
pushedBy.StoreTo = new List<ILVariable>(1); |
|
|
|
|
} |
|
|
|
|
pushedBy.StoreTo.Add(tmpVar); |
|
|
|
|
} |
|
|
|
|
if (arg.PushedBy.Count == 1) { |
|
|
|
|
if (byteCode.StackBefore[i].PushedBy.Length == 1) { |
|
|
|
|
allowInline[tmpVar] = true; |
|
|
|
|
} |
|
|
|
|
argIdx++; |
|
|
|
@ -482,10 +485,10 @@ namespace ICSharpCode.Decompiler.ILAst
@@ -482,10 +485,10 @@ namespace ICSharpCode.Decompiler.ILAst
|
|
|
|
|
|
|
|
|
|
// Add loads to the data structure; merge variables if necessary
|
|
|
|
|
foreach(ByteCode load in loads) { |
|
|
|
|
List<ByteCode> storedBy = load.VariablesBefore[variableIndex].StoredBy; |
|
|
|
|
if (storedBy.Count == 0) { |
|
|
|
|
ByteCode[] storedBy = load.VariablesBefore[variableIndex].StoredBy; |
|
|
|
|
if (storedBy.Length == 0) { |
|
|
|
|
throw new Exception("Load of uninitialized variable"); |
|
|
|
|
} else if (storedBy.Count == 1) { |
|
|
|
|
} else if (storedBy.Length == 1) { |
|
|
|
|
VariableInfo newVar = newVars.Where(v => v.Stores.Contains(storedBy[0])).Single(); |
|
|
|
|
newVar.Loads.Add(load); |
|
|
|
|
} else { |
|
|
|
@ -731,5 +734,16 @@ namespace ICSharpCode.Decompiler.ILAst
@@ -731,5 +734,16 @@ namespace ICSharpCode.Decompiler.ILAst
|
|
|
|
|
list.RemoveRange(start, count); |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static T[] Union<T>(this T[] a, T[] b) |
|
|
|
|
{ |
|
|
|
|
if (a.Length == 0) |
|
|
|
|
return b; |
|
|
|
|
if (b.Length == 0) |
|
|
|
|
return a; |
|
|
|
|
if (a.Length == 1 && b.Length == 1 && a[0].Equals(b[0])) |
|
|
|
|
return a; |
|
|
|
|
return Enumerable.Union(a, b).ToArray(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|