|
|
|
@ -12,27 +12,45 @@ namespace Decompiler
@@ -12,27 +12,45 @@ namespace Decompiler
|
|
|
|
|
{ |
|
|
|
|
public class ILAstBuilder |
|
|
|
|
{ |
|
|
|
|
class ILStack |
|
|
|
|
class StackSlot |
|
|
|
|
{ |
|
|
|
|
public class Slot |
|
|
|
|
public List<ByteCode> PushedBy; // Pushed by one of these; null element means exception pushed by CLR
|
|
|
|
|
public ILVariable LoadFrom; |
|
|
|
|
|
|
|
|
|
public StackSlot() |
|
|
|
|
{ |
|
|
|
|
public Instruction PushedBy; |
|
|
|
|
public TypeReference Type; |
|
|
|
|
|
|
|
|
|
public Slot(Instruction inst, TypeReference type) |
|
|
|
|
{ |
|
|
|
|
this.PushedBy = inst; |
|
|
|
|
this.Type = type; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public List<Slot> Items = new List<Slot>(); |
|
|
|
|
public StackSlot(ByteCode pushedBy) |
|
|
|
|
{ |
|
|
|
|
this.PushedBy = new List<ByteCode>(1); |
|
|
|
|
this.PushedBy.Add(pushedBy); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class ByteCode |
|
|
|
|
{ |
|
|
|
|
public ILLabel Label; // Non-null only if needed
|
|
|
|
|
public int Offset; |
|
|
|
|
public OpCode OpCode; |
|
|
|
|
public object Operand; |
|
|
|
|
public int? PopCount; // Null means pop all
|
|
|
|
|
public int PushCount; |
|
|
|
|
public string Name { get { return "IL_" + this.Offset.ToString("X2"); } } |
|
|
|
|
public ByteCode Next; |
|
|
|
|
public List<StackSlot> StackBefore; |
|
|
|
|
public List<ILVariable> StoreTo; |
|
|
|
|
|
|
|
|
|
public ILStack Clone() |
|
|
|
|
public List<StackSlot> CloneStack(int? popCount) |
|
|
|
|
{ |
|
|
|
|
ILStack clone = new ILStack(); |
|
|
|
|
foreach(Slot s in this.Items) { |
|
|
|
|
clone.Items.Add(new Slot(s.PushedBy, s.Type)); |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
@ -40,123 +58,237 @@ namespace Decompiler
@@ -40,123 +58,237 @@ namespace Decompiler
|
|
|
|
|
public override string ToString() |
|
|
|
|
{ |
|
|
|
|
StringBuilder sb = new StringBuilder(); |
|
|
|
|
bool first = true; |
|
|
|
|
foreach (Slot s in this.Items) { |
|
|
|
|
if (!first) sb.Append(", "); |
|
|
|
|
sb.Append(s.PushedBy.Offset.ToString("X")); |
|
|
|
|
first = false; |
|
|
|
|
sb.AppendFormat("{0}:{1} {2} {3}", this.Name, this.Label != null ? " *" : "", this.OpCode, this.Operand); |
|
|
|
|
if (this.StackBefore != null) { |
|
|
|
|
sb.Append(" StackBefore = {"); |
|
|
|
|
bool first = true; |
|
|
|
|
foreach (StackSlot slot in this.StackBefore) { |
|
|
|
|
if (!first) sb.Append(","); |
|
|
|
|
bool first2 = true; |
|
|
|
|
foreach(ByteCode pushedBy in slot.PushedBy) { |
|
|
|
|
if (!first2) sb.Append("|"); |
|
|
|
|
sb.AppendFormat("IL_{0:X2}", pushedBy.Offset); |
|
|
|
|
first2 = false; |
|
|
|
|
} |
|
|
|
|
first = false; |
|
|
|
|
} |
|
|
|
|
sb.Append("}"); |
|
|
|
|
} |
|
|
|
|
if (this.StoreTo != null && this.StoreTo.Count > 0) { |
|
|
|
|
sb.Append(" StoreTo = {"); |
|
|
|
|
bool first = true; |
|
|
|
|
foreach (ILVariable stackVar in this.StoreTo) { |
|
|
|
|
if (!first) sb.Append(","); |
|
|
|
|
sb.Append(stackVar.Name); |
|
|
|
|
first = false; |
|
|
|
|
} |
|
|
|
|
sb.Append("}"); |
|
|
|
|
} |
|
|
|
|
return sb.ToString(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
MethodDefinition methodDef; |
|
|
|
|
Dictionary<Instruction, ILStack> stackBefore = new Dictionary<Instruction, ILAstBuilder.ILStack>(); |
|
|
|
|
Dictionary<Instruction, ILLabel> labels = new Dictionary<Instruction, ILLabel>(); |
|
|
|
|
bool optimize; |
|
|
|
|
|
|
|
|
|
public List<ILNode> Build(MethodDefinition methodDef) |
|
|
|
|
Dictionary<Instruction, ByteCode> instrToByteCode = new Dictionary<Instruction, ByteCode>(); |
|
|
|
|
Dictionary<ILVariable, bool> allowInline = new Dictionary<ILVariable, bool>(); |
|
|
|
|
|
|
|
|
|
public List<ILNode> Build(MethodDefinition methodDef, bool optimize) |
|
|
|
|
{ |
|
|
|
|
this.methodDef = methodDef; |
|
|
|
|
this.optimize = optimize; |
|
|
|
|
|
|
|
|
|
// Make editable copy
|
|
|
|
|
List<Instruction> body = new List<Instruction>(methodDef.Body.Instructions); |
|
|
|
|
|
|
|
|
|
if (body.Count == 0) return new List<ILNode>(); |
|
|
|
|
if (methodDef.Body.Instructions.Count == 0) return new List<ILNode>(); |
|
|
|
|
|
|
|
|
|
StackAnalysis(body, methodDef); |
|
|
|
|
|
|
|
|
|
// Create branch labels for instructins; use the labels as branch operands
|
|
|
|
|
foreach (Instruction inst in body) { |
|
|
|
|
if (inst.Operand is Instruction[]) { |
|
|
|
|
foreach(Instruction target in (Instruction[])inst.Operand) { |
|
|
|
|
if (!labels.ContainsKey(target)) { |
|
|
|
|
labels[target] = new ILLabel() { Name = "IL_" + target.Offset.ToString("X2") }; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else if (inst.Operand is Instruction) { |
|
|
|
|
Instruction target = (Instruction)inst.Operand; |
|
|
|
|
if (!labels.ContainsKey(target)) { |
|
|
|
|
labels[target] = new ILLabel() { Name = "IL_" + target.Offset.ToString("X2") }; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
List<ByteCode> body = StackAnalysis(methodDef); |
|
|
|
|
|
|
|
|
|
List<ILNode> ast = ConvertToAst(body, methodDef.Body.ExceptionHandlers); |
|
|
|
|
|
|
|
|
|
return ast; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void StackAnalysis(List<Instruction> body, MethodDefinition methodDef) |
|
|
|
|
List<ByteCode> StackAnalysis(MethodDefinition methodDef) |
|
|
|
|
{ |
|
|
|
|
Queue<Instruction> agenda = new Queue<Instruction>(); |
|
|
|
|
// Create temporary structure for the stack analysis
|
|
|
|
|
List<ByteCode> body = new List<ByteCode>(methodDef.Body.Instructions.Count); |
|
|
|
|
foreach(Instruction inst in methodDef.Body.Instructions) { |
|
|
|
|
OpCode opCode = inst.OpCode; |
|
|
|
|
object operand = inst.Operand; |
|
|
|
|
MethodBodyRocks.ExpandMacro(ref opCode, ref operand, methodDef.Body); |
|
|
|
|
ByteCode byteCode = new ByteCode() { |
|
|
|
|
Offset = inst.Offset, |
|
|
|
|
OpCode = opCode, |
|
|
|
|
Operand = operand, |
|
|
|
|
PopCount = inst.GetPopCount(), |
|
|
|
|
PushCount = inst.GetPushCount() |
|
|
|
|
}; |
|
|
|
|
instrToByteCode[inst] = byteCode; |
|
|
|
|
body.Add(byteCode); |
|
|
|
|
} |
|
|
|
|
for (int i = 0; i < body.Count - 1; i++) { |
|
|
|
|
body[i].Next = body[i + 1]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Queue<ByteCode> agenda = new Queue<ByteCode>(); |
|
|
|
|
|
|
|
|
|
// Add known states
|
|
|
|
|
stackBefore[body[0]] = new ILStack(); |
|
|
|
|
body[0].StackBefore = new List<StackSlot>(); |
|
|
|
|
agenda.Enqueue(body[0]); |
|
|
|
|
|
|
|
|
|
if(methodDef.Body.HasExceptionHandlers) { |
|
|
|
|
foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) { |
|
|
|
|
stackBefore[ex.TryStart] = new ILStack(); |
|
|
|
|
agenda.Enqueue(ex.TryStart); |
|
|
|
|
ByteCode tryStart = instrToByteCode[ex.TryStart]; |
|
|
|
|
tryStart.StackBefore = new List<StackSlot>(); |
|
|
|
|
agenda.Enqueue(tryStart); |
|
|
|
|
|
|
|
|
|
ILStack stack = new ILStack(); |
|
|
|
|
stack.Items.Add(new ILStack.Slot(null, MyRocks.TypeException)); |
|
|
|
|
stackBefore[ex.HandlerStart] = stack; |
|
|
|
|
agenda.Enqueue(ex.HandlerStart); |
|
|
|
|
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) { |
|
|
|
|
handlerStart.StackBefore.Add(new StackSlot(null)); |
|
|
|
|
} |
|
|
|
|
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>(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Process agenda
|
|
|
|
|
while(agenda.Count > 0) { |
|
|
|
|
Instruction inst = agenda.Dequeue(); |
|
|
|
|
ByteCode byteCode = agenda.Dequeue(); |
|
|
|
|
|
|
|
|
|
// What is the effect of the instruction on the stack?
|
|
|
|
|
ILStack newStack = stackBefore[inst].Clone(); |
|
|
|
|
int popCount = inst.GetPopCount(); |
|
|
|
|
if (popCount == int.MaxValue) popCount = stackBefore[inst].Items.Count; // Pop all
|
|
|
|
|
List<TypeReference> typeArgs = new List<TypeReference>(); |
|
|
|
|
for (int i = newStack.Items.Count - popCount; i < newStack.Items.Count; i++) { |
|
|
|
|
typeArgs.Add(newStack.Items[i].Type); |
|
|
|
|
} |
|
|
|
|
TypeReference type; |
|
|
|
|
try { |
|
|
|
|
type = inst.GetTypeInternal(methodDef, typeArgs); |
|
|
|
|
} catch { |
|
|
|
|
type = MyRocks.TypeObject; |
|
|
|
|
} |
|
|
|
|
if (popCount > 0) { |
|
|
|
|
newStack.Items.RemoveRange(newStack.Items.Count - popCount, popCount); |
|
|
|
|
} |
|
|
|
|
int pushCount = inst.GetPushCount(); |
|
|
|
|
for (int i = 0; i < pushCount; i++) { |
|
|
|
|
newStack.Items.Add(new ILStack.Slot(inst, type)); |
|
|
|
|
// Calculate new stack
|
|
|
|
|
List<StackSlot> newStack = byteCode.CloneStack(byteCode.PopCount); |
|
|
|
|
for (int i = 0; i < byteCode.PushCount; i++) { |
|
|
|
|
newStack.Add(new StackSlot(byteCode)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Apply the state to any successors
|
|
|
|
|
List<Instruction> branchTargets = new List<Instruction>(); |
|
|
|
|
if (inst.OpCode.CanFallThough()) { |
|
|
|
|
branchTargets.Add(inst.Next); |
|
|
|
|
List<ByteCode> branchTargets = new List<ByteCode>(); |
|
|
|
|
if (byteCode.OpCode.CanFallThough()) { |
|
|
|
|
branchTargets.Add(byteCode.Next); |
|
|
|
|
} |
|
|
|
|
if (inst.OpCode.IsBranch()) { |
|
|
|
|
if (inst.Operand is Instruction[]) { |
|
|
|
|
branchTargets.AddRange((Instruction[])inst.Operand); |
|
|
|
|
if (byteCode.OpCode.IsBranch()) { |
|
|
|
|
if (byteCode.Operand is Instruction[]) { |
|
|
|
|
foreach(Instruction inst in (Instruction[])byteCode.Operand) { |
|
|
|
|
ByteCode target = instrToByteCode[inst]; |
|
|
|
|
branchTargets.Add(target); |
|
|
|
|
// The target of a branch must have label
|
|
|
|
|
if (target.Label == null) { |
|
|
|
|
target.Label = new ILLabel() { Name = target.Name }; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
branchTargets.Add((Instruction)inst.Operand); |
|
|
|
|
ByteCode target = instrToByteCode[(Instruction)byteCode.Operand]; |
|
|
|
|
branchTargets.Add(target); |
|
|
|
|
// The target of a branch must have label
|
|
|
|
|
if (target.Label == null) { |
|
|
|
|
target.Label = new ILLabel() { Name = target.Name }; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
foreach (Instruction branchTarget in branchTargets) { |
|
|
|
|
ILStack nextStack; |
|
|
|
|
if (stackBefore.TryGetValue(branchTarget, out nextStack)) { |
|
|
|
|
// TODO: Compare stacks
|
|
|
|
|
} else { |
|
|
|
|
stackBefore[branchTarget] = newStack; |
|
|
|
|
foreach (ByteCode branchTarget in branchTargets) { |
|
|
|
|
if (branchTarget.StackBefore == null) { |
|
|
|
|
branchTarget.StackBefore = newStack; |
|
|
|
|
// Do not share one stack for several bytecodes
|
|
|
|
|
if (branchTargets.Count > 1) { |
|
|
|
|
branchTarget.StackBefore = branchTarget.CloneStack(0); |
|
|
|
|
} |
|
|
|
|
agenda.Enqueue(branchTarget); |
|
|
|
|
} else { |
|
|
|
|
if (branchTarget.StackBefore.Count != newStack.Count) { |
|
|
|
|
throw new Exception("Inconsistent stack size at " + byteCode.Name); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Merge stacks
|
|
|
|
|
bool modified = false; |
|
|
|
|
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; |
|
|
|
|
modified = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (modified) { |
|
|
|
|
agenda.Enqueue(branchTarget); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Genertate temporary variables to replace stack
|
|
|
|
|
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++) { |
|
|
|
|
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) { |
|
|
|
|
// TODO: Handle exception variables
|
|
|
|
|
if (pushedBy != null) { |
|
|
|
|
if (pushedBy.StoreTo == null) { |
|
|
|
|
pushedBy.StoreTo = new List<ILVariable>(1); |
|
|
|
|
} |
|
|
|
|
pushedBy.StoreTo.Add(tmpVar); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (arg.PushedBy.Count == 1) { |
|
|
|
|
allowInline[tmpVar] = true; |
|
|
|
|
} |
|
|
|
|
argIdx++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Convert local varibles
|
|
|
|
|
List<ILVariable> vars = methodDef.Body.Variables.Select(v => new ILVariable() { Name = string.IsNullOrEmpty(v.Name) ? "var_" + v.Index : v.Name }).ToList(); |
|
|
|
|
int[] numReads = new int[vars.Count]; |
|
|
|
|
int[] numWrites = new int[vars.Count]; |
|
|
|
|
foreach(ByteCode byteCode in body) { |
|
|
|
|
if (byteCode.OpCode == OpCodes.Ldloc) { |
|
|
|
|
int index = ((VariableDefinition)byteCode.Operand).Index; |
|
|
|
|
byteCode.Operand = vars[index]; |
|
|
|
|
numReads[index]++; |
|
|
|
|
} |
|
|
|
|
if (byteCode.OpCode == OpCodes.Stloc) { |
|
|
|
|
int index = ((VariableDefinition)byteCode.Operand).Index; |
|
|
|
|
byteCode.Operand = vars[index]; |
|
|
|
|
numWrites[index]++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Find which variables we can inline
|
|
|
|
|
if (this.optimize) { |
|
|
|
|
for (int i = 0; i < vars.Count; i++) { |
|
|
|
|
if (numReads[i] == 1 && numWrites[i] == 1) { |
|
|
|
|
allowInline[vars[i]] = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Convert branch targets to labels
|
|
|
|
|
foreach(ByteCode byteCode in body) { |
|
|
|
|
if (byteCode.Operand is Instruction[]) { |
|
|
|
|
List<ILLabel> newOperand = new List<ILLabel>(); |
|
|
|
|
foreach(Instruction target in (Instruction[])byteCode.Operand) { |
|
|
|
|
newOperand.Add(instrToByteCode[target].Label); |
|
|
|
|
} |
|
|
|
|
byteCode.Operand = newOperand.ToArray(); |
|
|
|
|
} else if (byteCode.Operand is Instruction) { |
|
|
|
|
byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return body; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public List<ILNode> ConvertToAst(List<Instruction> body, IEnumerable<ExceptionHandler> ehs) |
|
|
|
|
List<ILNode> ConvertToAst(List<ByteCode> body, IEnumerable<ExceptionHandler> ehs) |
|
|
|
|
{ |
|
|
|
|
List<ILNode> ast = new List<ILNode>(); |
|
|
|
|
|
|
|
|
@ -187,9 +319,9 @@ namespace Decompiler
@@ -187,9 +319,9 @@ namespace Decompiler
|
|
|
|
|
tryCatchBlock.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>(); |
|
|
|
|
foreach(ExceptionHandler eh in handlers) { |
|
|
|
|
int start; |
|
|
|
|
for (start = 0; body[start] != eh.HandlerStart; start++); |
|
|
|
|
for (start = 0; body[start].Offset != eh.HandlerStart.Offset; start++); |
|
|
|
|
int end; |
|
|
|
|
for (end = 0; body[end] != eh.HandlerEnd; end++); |
|
|
|
|
for (end = 0; body[end].Offset != eh.HandlerEnd.Offset; end++); |
|
|
|
|
int count = end - start; |
|
|
|
|
List<ExceptionHandler> nestedEHs = ehs.Where(e => (start <= e.TryStart.Offset && e.TryEnd.Offset < end) || (start < e.TryStart.Offset && e.TryEnd.Offset <= end)).ToList(); |
|
|
|
|
List<ILNode> handlerAst = ConvertToAst(body.CutRange(start, count), nestedEHs); |
|
|
|
@ -216,73 +348,62 @@ namespace Decompiler
@@ -216,73 +348,62 @@ namespace Decompiler
|
|
|
|
|
return ast; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public List<ILNode> ConvertToAst(List<Instruction> body) |
|
|
|
|
List<ILNode> ConvertToAst(List<ByteCode> body) |
|
|
|
|
{ |
|
|
|
|
List<ILNode> ast = new List<ILNode>(); |
|
|
|
|
|
|
|
|
|
// Convert stack-based IL code to ILAst tree
|
|
|
|
|
foreach(Instruction inst in body) { |
|
|
|
|
OpCode opCode = inst.OpCode; |
|
|
|
|
object operand = inst.Operand; |
|
|
|
|
foreach(ByteCode byteCode in body) { |
|
|
|
|
OpCode opCode = byteCode.OpCode; |
|
|
|
|
object operand = byteCode.Operand; |
|
|
|
|
|
|
|
|
|
MethodBodyRocks.ExpandMacro(ref opCode, ref operand, methodDef.Body); |
|
|
|
|
|
|
|
|
|
ILExpression expr = new ILExpression(opCode, operand); |
|
|
|
|
|
|
|
|
|
// Label for this instruction
|
|
|
|
|
ILLabel label; |
|
|
|
|
if (labels.TryGetValue(inst, out label)) { |
|
|
|
|
ast.Add(label); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Branch using labels
|
|
|
|
|
if (inst.Operand is Instruction[]) { |
|
|
|
|
List<ILLabel> newOperand = new List<ILLabel>(); |
|
|
|
|
foreach(Instruction target in (Instruction[])inst.Operand) { |
|
|
|
|
newOperand.Add(labels[target]); |
|
|
|
|
} |
|
|
|
|
expr.Operand = newOperand.ToArray(); |
|
|
|
|
} else if (inst.Operand is Instruction) { |
|
|
|
|
expr.Operand = labels[(Instruction)inst.Operand]; |
|
|
|
|
if (byteCode.Label != null) { |
|
|
|
|
ast.Add(byteCode.Label); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Reference arguments using temporary variables
|
|
|
|
|
ILStack stack = stackBefore[inst]; |
|
|
|
|
int popCount = inst.GetPopCount(); |
|
|
|
|
if (popCount == int.MaxValue) popCount = stackBefore[inst].Items.Count; // Pop all
|
|
|
|
|
for (int i = stack.Items.Count - popCount; i < stack.Items.Count; i++) { |
|
|
|
|
Instruction pushedBy = stack.Items[i].PushedBy; |
|
|
|
|
if (pushedBy != null) { |
|
|
|
|
ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, new ILStackVariable() { Name = "expr" + pushedBy.Offset.ToString("X2") }); |
|
|
|
|
int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count; |
|
|
|
|
for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) { |
|
|
|
|
StackSlot slot = byteCode.StackBefore[i]; |
|
|
|
|
if (slot.PushedBy != null) { |
|
|
|
|
ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, slot.LoadFrom); |
|
|
|
|
expr.Arguments.Add(ldExpr); |
|
|
|
|
} else { |
|
|
|
|
ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, new ILStackVariable() { Name = "exception" }); |
|
|
|
|
ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, new ILVariable() { Name = "ex", IsGenerated = true }); |
|
|
|
|
expr.Arguments.Add(ldExpr); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// If the bytecode pushes anything store the result in temporary variable
|
|
|
|
|
int pushCount = inst.GetPushCount(); |
|
|
|
|
if (pushCount > 0) { |
|
|
|
|
ILExpression stExpr = new ILExpression(OpCodes.Stloc, new ILStackVariable() { Name = "expr" + inst.Offset.ToString("X2"), RefCount = pushCount }); |
|
|
|
|
stExpr.Arguments.Add(expr); |
|
|
|
|
expr = stExpr; |
|
|
|
|
// Store the result to temporary variable(s) if needed
|
|
|
|
|
if (byteCode.StoreTo == null || byteCode.StoreTo.Count == 0) { |
|
|
|
|
ast.Add(expr); |
|
|
|
|
} else if (byteCode.StoreTo.Count == 1) { |
|
|
|
|
ast.Add(new ILExpression(OpCodes.Stloc, byteCode.StoreTo[0], expr)); |
|
|
|
|
} else { |
|
|
|
|
ILVariable tmpVar = new ILVariable() { Name = "expr_" + byteCode.Offset.ToString("X2"), IsGenerated = true }; |
|
|
|
|
ast.Add(new ILExpression(OpCodes.Stloc, tmpVar, expr)); |
|
|
|
|
foreach(ILVariable storeTo in byteCode.StoreTo) { |
|
|
|
|
ast.Add(new ILExpression(OpCodes.Stloc, storeTo, new ILExpression(OpCodes.Ldloc, tmpVar))); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ast.Add(expr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Try to in-line stloc / ldloc pairs
|
|
|
|
|
for(int i = 0; i < ast.Count - 1; i++) { |
|
|
|
|
if (i < 0) continue; |
|
|
|
|
|
|
|
|
|
ILExpression expr = ast[i] as ILExpression; |
|
|
|
|
ILExpression currExpr = ast[i] as ILExpression; |
|
|
|
|
ILExpression nextExpr = ast[i + 1] as ILExpression; |
|
|
|
|
|
|
|
|
|
if (expr != null && nextExpr != null && expr.OpCode.Code == Code.Stloc && expr.Operand is ILStackVariable) { |
|
|
|
|
if (currExpr != null && nextExpr != null && currExpr.OpCode.Code == Code.Stloc) { |
|
|
|
|
|
|
|
|
|
// If the next expression is stloc, look inside
|
|
|
|
|
if (nextExpr.OpCode.Code == Code.Stloc && nextExpr.Operand is ILStackVariable) { |
|
|
|
|
// If the next expression is generated stloc, look inside
|
|
|
|
|
if (nextExpr.OpCode.Code == Code.Stloc && ((ILVariable)nextExpr.Operand).IsGenerated) { |
|
|
|
|
nextExpr = nextExpr.Arguments[0]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -290,22 +411,19 @@ namespace Decompiler
@@ -290,22 +411,19 @@ namespace Decompiler
|
|
|
|
|
for(int j = 0; j < nextExpr.Arguments.Count; j++) { |
|
|
|
|
ILExpression arg = nextExpr.Arguments[j]; |
|
|
|
|
|
|
|
|
|
// TODO: Check if duplicating the dup opcode has side-effects
|
|
|
|
|
|
|
|
|
|
if (arg.OpCode.Code == Code.Ldloc && arg.Operand is ILStackVariable) { |
|
|
|
|
ILStackVariable stVar = (ILStackVariable)expr.Operand; |
|
|
|
|
ILStackVariable ldVar = (ILStackVariable)arg.Operand; |
|
|
|
|
if (stVar.Name == ldVar.Name) { |
|
|
|
|
stVar.RefCount--; |
|
|
|
|
if (stVar.RefCount <= 0) { |
|
|
|
|
ast.RemoveAt(i); |
|
|
|
|
} |
|
|
|
|
nextExpr.Arguments[j] = expr.Arguments[0]; // Inline the stloc body
|
|
|
|
|
// 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
|
|
|
|
|
if (arg.OpCode.Code == Code.Ldloc) { |
|
|
|
|
bool canInline; |
|
|
|
|
allowInline.TryGetValue((ILVariable)arg.Operand, out canInline); |
|
|
|
|
if (arg.Operand == currExpr.Operand && canInline) { |
|
|
|
|
ast.RemoveAt(i); |
|
|
|
|
nextExpr.Arguments[j] = currExpr.Arguments[0]; // Inline the stloc body
|
|
|
|
|
i -= 2; // Try the same index again
|
|
|
|
|
break; // Found
|
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
break; // This argument might have side effects so we can not move the 'expr' after it.
|
|
|
|
|
break; // Side-effects
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|