|
|
|
@ -12,8 +12,8 @@ namespace Decompiler
@@ -12,8 +12,8 @@ namespace Decompiler
|
|
|
|
|
{ |
|
|
|
|
class StackSlot |
|
|
|
|
{ |
|
|
|
|
public List<ByteCode> PushedBy; // Pushed by one of these; null element means exception pushed by CLR
|
|
|
|
|
public ILVariable LoadFrom; |
|
|
|
|
public List<ByteCode> PushedBy; // One of those
|
|
|
|
|
public ILVariable LoadFrom; // Where can we get the value from in AST
|
|
|
|
|
|
|
|
|
|
public StackSlot() |
|
|
|
|
{ |
|
|
|
@ -24,6 +24,61 @@ namespace Decompiler
@@ -24,6 +24,61 @@ namespace Decompiler
|
|
|
|
|
this.PushedBy = new List<ByteCode>(1); |
|
|
|
|
this.PushedBy.Add(pushedBy); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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 clone; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class VariableSlot |
|
|
|
|
{ |
|
|
|
|
public static List<ByteCode> Empty = new List<ByteCode>(); |
|
|
|
|
|
|
|
|
|
public List<ByteCode> StoredBy = Empty; // One of those
|
|
|
|
|
public bool StoredByAll; // Overestimate which is useful for exceptional control flow.
|
|
|
|
|
|
|
|
|
|
public static VariableSlot[] CloneVariableState(VariableSlot[] state) |
|
|
|
|
{ |
|
|
|
|
VariableSlot[] clone = new ILAstBuilder.VariableSlot[state.Length]; |
|
|
|
|
if (VariableSlot.Empty.Count > 0) |
|
|
|
|
throw new Exception("Constant data corrupted"); |
|
|
|
|
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 |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
return clone; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static VariableSlot[] MakeEmptyState(int varCount) |
|
|
|
|
{ |
|
|
|
|
VariableSlot[] emptyVariableState = new VariableSlot[varCount]; |
|
|
|
|
for (int i = 0; i < emptyVariableState.Length; i++) { |
|
|
|
|
emptyVariableState[i] = new VariableSlot(); |
|
|
|
|
} |
|
|
|
|
return emptyVariableState; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static VariableSlot[] MakeFullState(int varCount) |
|
|
|
|
{ |
|
|
|
|
VariableSlot[] unknownVariableState = new VariableSlot[varCount]; |
|
|
|
|
for (int i = 0; i < unknownVariableState.Length; i++) { |
|
|
|
|
unknownVariableState[i] = new VariableSlot() { StoredByAll = true }; |
|
|
|
|
} |
|
|
|
|
return unknownVariableState; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class ByteCode |
|
|
|
@ -39,26 +94,23 @@ namespace Decompiler
@@ -39,26 +94,23 @@ namespace Decompiler
|
|
|
|
|
public ByteCode Next; |
|
|
|
|
public Instruction[] Prefixes; // Non-null only if needed
|
|
|
|
|
public List<StackSlot> StackBefore; |
|
|
|
|
public List<ILVariable> StoreTo; |
|
|
|
|
public List<ILVariable> StoreTo; // Store result of instruction to those AST variables
|
|
|
|
|
public VariableSlot[] VariablesBefore; |
|
|
|
|
|
|
|
|
|
public List<StackSlot> CloneStack(int? popCount) |
|
|
|
|
{ |
|
|
|
|
List<StackSlot> clone = new List<StackSlot>(); |
|
|
|
|
if (popCount.HasValue) { |
|
|
|
|
if (popCount.Value > this.StackBefore.Count) { |
|
|
|
|
throw new Exception("Can not pop - the stack is empty"); |
|
|
|
|
} |
|
|
|
|
for(int i = 0; i < this.StackBefore.Count - popCount.Value; i++) { |
|
|
|
|
clone.Add(new StackSlot() { PushedBy = new List<ByteCode>(this.StackBefore[i].PushedBy) }); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return clone; |
|
|
|
|
} |
|
|
|
|
public VariableDefinition OperandAsVariable { get { return (VariableDefinition)this.Operand; } } |
|
|
|
|
|
|
|
|
|
public override string ToString() |
|
|
|
|
{ |
|
|
|
|
StringBuilder sb = new StringBuilder(); |
|
|
|
|
sb.AppendFormat("{0}:{1} ", this.Name, this.Label != null ? " *" : ""); |
|
|
|
|
|
|
|
|
|
// Label
|
|
|
|
|
sb.Append(this.Name); |
|
|
|
|
sb.Append(':'); |
|
|
|
|
if (this.Label != null) |
|
|
|
|
sb.Append('*'); |
|
|
|
|
|
|
|
|
|
// Name
|
|
|
|
|
sb.Append(' '); |
|
|
|
|
if (this.Prefixes != null) { |
|
|
|
|
foreach (var prefix in this.Prefixes) { |
|
|
|
|
sb.Append(prefix.OpCode.Name); |
|
|
|
@ -66,7 +118,17 @@ namespace Decompiler
@@ -66,7 +118,17 @@ namespace Decompiler
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
sb.Append(this.Code.GetName()); |
|
|
|
|
if (this.Operand is ILLabel) { |
|
|
|
|
|
|
|
|
|
if (this.Operand != null) { |
|
|
|
|
sb.Append(' '); |
|
|
|
|
if (this.Operand is Instruction) { |
|
|
|
|
sb.Append("IL_" + ((Instruction)this.Operand).Offset.ToString("X2")); |
|
|
|
|
} else if (this.Operand is Instruction[]) { |
|
|
|
|
foreach(Instruction inst in (Instruction[])this.Operand) { |
|
|
|
|
sb.Append("IL_" + inst.Offset.ToString("X2")); |
|
|
|
|
sb.Append(" "); |
|
|
|
|
} |
|
|
|
|
} else if (this.Operand is ILLabel) { |
|
|
|
|
sb.Append(((ILLabel)this.Operand).Name); |
|
|
|
|
} else if (this.Operand is ILLabel[]) { |
|
|
|
|
foreach(ILLabel label in (ILLabel[])this.Operand) { |
|
|
|
@ -76,6 +138,8 @@ namespace Decompiler
@@ -76,6 +138,8 @@ namespace Decompiler
|
|
|
|
|
} else { |
|
|
|
|
sb.Append(this.Operand.ToString()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (this.StackBefore != null) { |
|
|
|
|
sb.Append(" StackBefore={"); |
|
|
|
|
bool first = true; |
|
|
|
@ -91,6 +155,7 @@ namespace Decompiler
@@ -91,6 +155,7 @@ namespace Decompiler
|
|
|
|
|
} |
|
|
|
|
sb.Append("}"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (this.StoreTo != null && this.StoreTo.Count > 0) { |
|
|
|
|
sb.Append(" StoreTo={"); |
|
|
|
|
bool first = true; |
|
|
|
@ -101,6 +166,29 @@ namespace Decompiler
@@ -101,6 +166,29 @@ namespace Decompiler
|
|
|
|
|
} |
|
|
|
|
sb.Append("}"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (this.VariablesBefore != null) { |
|
|
|
|
sb.Append(" VarsBefore={"); |
|
|
|
|
bool first = true; |
|
|
|
|
foreach (VariableSlot varSlot in this.VariablesBefore) { |
|
|
|
|
if (!first) sb.Append(","); |
|
|
|
|
if (varSlot.StoredByAll) { |
|
|
|
|
sb.Append("*"); |
|
|
|
|
} else if (varSlot.StoredBy.Count == 0) { |
|
|
|
|
sb.Append("_"); |
|
|
|
|
} else { |
|
|
|
|
bool first2 = true; |
|
|
|
|
foreach (ByteCode storedBy in varSlot.StoredBy) { |
|
|
|
|
if (!first2) sb.Append("|"); |
|
|
|
|
sb.AppendFormat("IL_{0:X2}", storedBy.Offset); |
|
|
|
|
first2 = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
first = false; |
|
|
|
|
} |
|
|
|
|
sb.Append("}"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return sb.ToString(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -167,18 +255,13 @@ namespace Decompiler
@@ -167,18 +255,13 @@ namespace Decompiler
|
|
|
|
|
body[i].Next = body[i + 1]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Queue<ByteCode> agenda = new Queue<ByteCode>(); |
|
|
|
|
Stack<ByteCode> agenda = new Stack<ByteCode>(); |
|
|
|
|
|
|
|
|
|
// Add known states
|
|
|
|
|
body[0].StackBefore = new List<StackSlot>(); |
|
|
|
|
agenda.Enqueue(body[0]); |
|
|
|
|
int varCount = methodDef.Body.Variables.Count; |
|
|
|
|
|
|
|
|
|
// Add known states
|
|
|
|
|
if(methodDef.Body.HasExceptionHandlers) { |
|
|
|
|
foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) { |
|
|
|
|
ByteCode tryStart = instrToByteCode[ex.TryStart]; |
|
|
|
|
tryStart.StackBefore = new List<StackSlot>(); |
|
|
|
|
agenda.Enqueue(tryStart); |
|
|
|
|
|
|
|
|
|
ByteCode handlerStart = instrToByteCode[ex.HandlerType == ExceptionHandlerType.Filter ? ex.FilterStart : ex.HandlerStart]; |
|
|
|
|
handlerStart.StackBefore = new List<StackSlot>(); |
|
|
|
|
if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter) { |
|
|
|
@ -191,27 +274,39 @@ namespace Decompiler
@@ -191,27 +274,39 @@ namespace Decompiler
|
|
|
|
|
ldexceptions[ex] = ldexception; |
|
|
|
|
handlerStart.StackBefore.Add(new StackSlot(ldexception)); |
|
|
|
|
} |
|
|
|
|
agenda.Enqueue(handlerStart); |
|
|
|
|
|
|
|
|
|
// Control flow is not required to reach endfilter
|
|
|
|
|
if (ex.HandlerType == ExceptionHandlerType.Filter) { |
|
|
|
|
ByteCode endFilter = instrToByteCode[ex.FilterEnd.Previous]; |
|
|
|
|
endFilter.StackBefore = new List<StackSlot>(); |
|
|
|
|
} |
|
|
|
|
handlerStart.VariablesBefore = VariableSlot.MakeFullState(varCount); |
|
|
|
|
agenda.Push(handlerStart); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
body[0].StackBefore = new List<StackSlot>(); |
|
|
|
|
body[0].VariablesBefore = VariableSlot.MakeEmptyState(varCount); |
|
|
|
|
agenda.Push(body[0]); |
|
|
|
|
|
|
|
|
|
// Process agenda
|
|
|
|
|
while(agenda.Count > 0) { |
|
|
|
|
ByteCode byteCode = agenda.Dequeue(); |
|
|
|
|
ByteCode byteCode = agenda.Pop(); |
|
|
|
|
|
|
|
|
|
// Calculate new stack
|
|
|
|
|
List<StackSlot> newStack = byteCode.CloneStack(byteCode.PopCount); |
|
|
|
|
List<StackSlot> newStack = StackSlot.CloneStack(byteCode.StackBefore, byteCode.PopCount); |
|
|
|
|
for (int i = 0; i < byteCode.PushCount; i++) { |
|
|
|
|
newStack.Add(new StackSlot(byteCode)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Apply the state to any successors
|
|
|
|
|
// Calculate new variable state
|
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// After the leave, finally block might have touched the variables
|
|
|
|
|
if (byteCode.Code == ILCode.Leave) { |
|
|
|
|
newVariableState = VariableSlot.MakeFullState(varCount); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Find all successors
|
|
|
|
|
List<ByteCode> branchTargets = new List<ByteCode>(); |
|
|
|
|
if (byteCode.Code.CanFallThough()) { |
|
|
|
|
branchTargets.Add(byteCode.Next); |
|
|
|
@ -233,21 +328,30 @@ namespace Decompiler
@@ -233,21 +328,30 @@ namespace Decompiler
|
|
|
|
|
target.Label = new ILLabel() { Name = target.Name }; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Apply the state to successors
|
|
|
|
|
foreach (ByteCode branchTarget in branchTargets) { |
|
|
|
|
if (branchTarget.StackBefore == null) { |
|
|
|
|
if (branchTarget.StackBefore == null && branchTarget.VariablesBefore == null) { |
|
|
|
|
if (branchTargets.Count == 1) { |
|
|
|
|
branchTarget.StackBefore = newStack; |
|
|
|
|
// Do not share one stack for several bytecodes
|
|
|
|
|
if (branchTargets.Count > 1) { |
|
|
|
|
branchTarget.StackBefore = branchTarget.CloneStack(0); |
|
|
|
|
branchTarget.VariablesBefore = newVariableState; |
|
|
|
|
} else { |
|
|
|
|
// Do not share data for several bytecodes
|
|
|
|
|
branchTarget.StackBefore = StackSlot.CloneStack(newStack, 0); |
|
|
|
|
branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState); |
|
|
|
|
} |
|
|
|
|
agenda.Enqueue(branchTarget); |
|
|
|
|
agenda.Push(branchTarget); |
|
|
|
|
} else { |
|
|
|
|
if (branchTarget.StackBefore.Count != newStack.Count) { |
|
|
|
|
throw new Exception("Inconsistent stack size at " + byteCode.Name); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Merge stacks
|
|
|
|
|
// Be careful not to change our new data - it might be reused for several branch targets.
|
|
|
|
|
// In general, be careful that two bytecodes never share data structures.
|
|
|
|
|
|
|
|
|
|
bool modified = false; |
|
|
|
|
|
|
|
|
|
// 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(); |
|
|
|
@ -257,8 +361,28 @@ namespace Decompiler
@@ -257,8 +361,28 @@ namespace Decompiler
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Merge variables - modify the target
|
|
|
|
|
for (int i = 0; i < newVariableState.Length; i++) { |
|
|
|
|
VariableSlot oldSlot = branchTarget.VariablesBefore[i]; |
|
|
|
|
VariableSlot newSlot = newVariableState[i]; |
|
|
|
|
// All can not be unioned further
|
|
|
|
|
if (!oldSlot.StoredByAll) { |
|
|
|
|
if (newSlot.StoredByAll) { |
|
|
|
|
oldSlot.StoredByAll = true; |
|
|
|
|
modified = true; |
|
|
|
|
} else { |
|
|
|
|
List<ByteCode> oldStoredBy = oldSlot.StoredBy; |
|
|
|
|
List<ByteCode> newStoredBy = oldStoredBy.Union(newSlot.StoredBy).ToList(); |
|
|
|
|
if (newStoredBy.Count > oldStoredBy.Count) { |
|
|
|
|
oldSlot.StoredBy = newStoredBy; |
|
|
|
|
modified = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (modified) { |
|
|
|
|
agenda.Enqueue(branchTarget); |
|
|
|
|
agenda.Push(branchTarget); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -288,36 +412,8 @@ namespace Decompiler
@@ -288,36 +412,8 @@ namespace Decompiler
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Convert local varibles
|
|
|
|
|
Variables = methodDef.Body.Variables.Select(v => new ILVariable() { Name = string.IsNullOrEmpty(v.Name) ? "var_" + v.Index : v.Name, Type = v.VariableType }).ToList(); |
|
|
|
|
int[] numReads = new int[Variables.Count]; |
|
|
|
|
int[] numWrites = new int[Variables.Count]; |
|
|
|
|
foreach(ByteCode byteCode in body) { |
|
|
|
|
if (byteCode.Code == ILCode.Ldloc) { |
|
|
|
|
int index = ((VariableDefinition)byteCode.Operand).Index; |
|
|
|
|
byteCode.Operand = Variables[index]; |
|
|
|
|
numReads[index]++; |
|
|
|
|
} else if (byteCode.Code == ILCode.Stloc) { |
|
|
|
|
int index = ((VariableDefinition)byteCode.Operand).Index; |
|
|
|
|
byteCode.Operand = Variables[index]; |
|
|
|
|
numWrites[index]++; |
|
|
|
|
} else if (byteCode.Code == ILCode.Ldloca) { |
|
|
|
|
int index = ((VariableDefinition)byteCode.Operand).Index; |
|
|
|
|
byteCode.Operand = Variables[index]; |
|
|
|
|
// ldloca leads to an unknown numbers of reads/writes, so ensure we don't inline the variable
|
|
|
|
|
numReads[index] += 2; |
|
|
|
|
numWrites[index] += 2; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Find which variables we can inline
|
|
|
|
|
if (this.optimize) { |
|
|
|
|
for (int i = 0; i < Variables.Count; i++) { |
|
|
|
|
if (numReads[i] == 1 && numWrites[i] == 1) { |
|
|
|
|
allowInline[Variables[i]] = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Split and convert the normal local variables
|
|
|
|
|
ConvertLocalVariables(body); |
|
|
|
|
|
|
|
|
|
// Convert branch targets to labels
|
|
|
|
|
foreach(ByteCode byteCode in body) { |
|
|
|
@ -335,6 +431,108 @@ namespace Decompiler
@@ -335,6 +431,108 @@ namespace Decompiler
|
|
|
|
|
return body; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class VariableInfo |
|
|
|
|
{ |
|
|
|
|
public ILVariable Variable; |
|
|
|
|
public List<ByteCode> Stores; |
|
|
|
|
public List<ByteCode> Loads; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// If possible, separates local variables into several independent variables.
|
|
|
|
|
/// It should undo any compilers merging.
|
|
|
|
|
/// </summary>
|
|
|
|
|
void ConvertLocalVariables(List<ByteCode> body) |
|
|
|
|
{ |
|
|
|
|
if (optimize) { |
|
|
|
|
int varCount = methodDef.Body.Variables.Count; |
|
|
|
|
this.Variables = new List<ILVariable>(varCount * 2); |
|
|
|
|
|
|
|
|
|
for(int variableIndex = 0; variableIndex < varCount; variableIndex++) { |
|
|
|
|
// Find all stores and loads for this variable
|
|
|
|
|
List<ByteCode> stores = body.Where(b => b.Code == ILCode.Stloc && b.Operand is VariableDefinition && b.OperandAsVariable.Index == variableIndex).ToList(); |
|
|
|
|
List<ByteCode> loads = body.Where(b => (b.Code == ILCode.Ldloc || b.Code == ILCode.Ldloca) && b.Operand is VariableDefinition && b.OperandAsVariable.Index == variableIndex).ToList(); |
|
|
|
|
TypeReference varType = methodDef.Body.Variables[variableIndex].VariableType; |
|
|
|
|
|
|
|
|
|
List<VariableInfo> newVars; |
|
|
|
|
|
|
|
|
|
// If any of the loads is from "all", use single variable
|
|
|
|
|
// If any of the loads is ldloca, fallback to single variable as well
|
|
|
|
|
if (loads.Any(b => b.VariablesBefore[variableIndex].StoredByAll || b.Code == ILCode.Ldloca)) { |
|
|
|
|
newVars = new List<VariableInfo>(1) { new VariableInfo() { |
|
|
|
|
Variable = new ILVariable() { |
|
|
|
|
Name = "var_" + variableIndex, |
|
|
|
|
Type = varType, |
|
|
|
|
OriginalVariable = methodDef.Body.Variables[variableIndex] |
|
|
|
|
}, |
|
|
|
|
Stores = stores, |
|
|
|
|
Loads = loads |
|
|
|
|
}}; |
|
|
|
|
} else { |
|
|
|
|
// Create a new variable for each store
|
|
|
|
|
newVars = stores.Select(st => new VariableInfo() { |
|
|
|
|
Variable = new ILVariable() { |
|
|
|
|
Name = "var_" + variableIndex + "_" + st.Offset.ToString("X2"), |
|
|
|
|
Type = varType, |
|
|
|
|
OriginalVariable = methodDef.Body.Variables[variableIndex] |
|
|
|
|
}, |
|
|
|
|
Stores = new List<ByteCode>() {st}, |
|
|
|
|
Loads = new List<ByteCode>() |
|
|
|
|
}).ToList(); |
|
|
|
|
|
|
|
|
|
// 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) { |
|
|
|
|
throw new Exception("Load of uninitialized variable"); |
|
|
|
|
} else if (storedBy.Count == 1) { |
|
|
|
|
VariableInfo newVar = newVars.Where(v => v.Stores.Contains(storedBy[0])).Single(); |
|
|
|
|
newVar.Loads.Add(load); |
|
|
|
|
} else { |
|
|
|
|
List<VariableInfo> mergeVars = newVars.Where(v => v.Stores.Union(storedBy).Any()).ToList(); |
|
|
|
|
VariableInfo mergedVar = new VariableInfo() { |
|
|
|
|
Variable = mergeVars[0].Variable, |
|
|
|
|
Stores = mergeVars.SelectMany(v => v.Stores).ToList(), |
|
|
|
|
Loads = mergeVars.SelectMany(v => v.Loads).ToList() |
|
|
|
|
}; |
|
|
|
|
mergedVar.Loads.Add(load); |
|
|
|
|
newVars = newVars.Except(mergeVars).ToList(); |
|
|
|
|
newVars.Add(mergedVar); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Permit inlining
|
|
|
|
|
foreach(VariableInfo newVar in newVars) { |
|
|
|
|
if (newVar.Stores.Count == 1 && newVar.Loads.Count == 1) { |
|
|
|
|
allowInline[newVar.Variable] = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Set bytecode operands
|
|
|
|
|
foreach(VariableInfo newVar in newVars) { |
|
|
|
|
foreach(ByteCode store in newVar.Stores) { |
|
|
|
|
store.Operand = newVar.Variable; |
|
|
|
|
} |
|
|
|
|
foreach(ByteCode load in newVar.Loads) { |
|
|
|
|
load.Operand = newVar.Variable; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Record new variables to global list
|
|
|
|
|
this.Variables.AddRange(newVars.Select(v => v.Variable)); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
this.Variables = methodDef.Body.Variables.Select(v => new ILVariable() { Name = string.IsNullOrEmpty(v.Name) ? "var_" + v.Index : v.Name, Type = v.VariableType, OriginalVariable = v }).ToList(); |
|
|
|
|
foreach(ByteCode byteCode in body) { |
|
|
|
|
if (byteCode.Code == ILCode.Ldloc || byteCode.Code == ILCode.Stloc || byteCode.Code == ILCode.Ldloca) { |
|
|
|
|
int index = ((VariableDefinition)byteCode.Operand).Index; |
|
|
|
|
byteCode.Operand = this.Variables[index]; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
List<ILNode> ConvertToAst(List<ByteCode> body, HashSet<ExceptionHandler> ehs) |
|
|
|
|
{ |
|
|
|
|
List<ILNode> ast = new List<ILNode>(); |
|
|
|
@ -490,6 +688,7 @@ namespace Decompiler
@@ -490,6 +688,7 @@ namespace Decompiler
|
|
|
|
|
|
|
|
|
|
// We are moving the expression evaluation past the other aguments.
|
|
|
|
|
// It is ok to pass ldloc because the expression can not contain stloc and thus the ldcoc will still return the same value
|
|
|
|
|
// Do not inline ldloca
|
|
|
|
|
if (arg.Code == ILCode.Ldloc) { |
|
|
|
|
if (arg.Operand == currExpr.Operand) { |
|
|
|
|
bool canInline; |
|
|
|
@ -500,6 +699,9 @@ namespace Decompiler
@@ -500,6 +699,9 @@ namespace Decompiler
|
|
|
|
|
currExpr.Arguments[0].ILRanges.AddRange(currExpr.ILRanges); |
|
|
|
|
currExpr.Arguments[0].ILRanges.AddRange(nextExpr.Arguments[j].ILRanges); |
|
|
|
|
|
|
|
|
|
// Remove from global list, if present
|
|
|
|
|
this.Variables.Remove((ILVariable)arg.Operand); |
|
|
|
|
|
|
|
|
|
ast.RemoveAt(i); |
|
|
|
|
nextExpr.Arguments[j] = currExpr.Arguments[0]; // Inline the stloc body
|
|
|
|
|
i -= 2; // Try the same index again
|
|
|
|
|