mirror of https://github.com/icsharpcode/ILSpy.git
Browse Source
Verify that the stack sizes are consistent - ie at a given location the stack size is statically known and constant. Keep track of instruction which pushed a given value on the stack. (Not handling the potential problem at control merge points - this problem does not occur in the quick sort algorithm) The state of the stack is outputted as a comment in the source codepull/1/head^2
4 changed files with 293 additions and 31 deletions
@ -0,0 +1,191 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
|
||||||
|
using Ast = ICSharpCode.NRefactory.Ast; |
||||||
|
using ICSharpCode.NRefactory.Ast; |
||||||
|
|
||||||
|
using Cecil = Mono.Cecil; |
||||||
|
using Mono.Cecil; |
||||||
|
using Mono.Cecil.Cil; |
||||||
|
|
||||||
|
namespace Decompiler |
||||||
|
{ |
||||||
|
// Imutable
|
||||||
|
public struct CilStackSlot { |
||||||
|
Instruction allocadedBy; |
||||||
|
|
||||||
|
public Instruction AllocadedBy { |
||||||
|
get { return allocadedBy; } |
||||||
|
} |
||||||
|
|
||||||
|
public CilStackSlot(Instruction allocadedBy) |
||||||
|
{ |
||||||
|
this.allocadedBy = allocadedBy; |
||||||
|
} |
||||||
|
|
||||||
|
public override int GetHashCode() |
||||||
|
{ |
||||||
|
int hashCode = 0; |
||||||
|
if (allocadedBy != null) hashCode ^= allocadedBy.GetHashCode(); |
||||||
|
return hashCode; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool Equals(object obj) |
||||||
|
{ |
||||||
|
if (!(obj is CilStackSlot)) return false; |
||||||
|
CilStackSlot myCilStackSlot = (CilStackSlot)obj; |
||||||
|
return object.Equals(this.allocadedBy, myCilStackSlot.allocadedBy); |
||||||
|
} |
||||||
|
|
||||||
|
public override string ToString() |
||||||
|
{ |
||||||
|
if (allocadedBy == null) { |
||||||
|
return "<??>"; |
||||||
|
} else { |
||||||
|
return string.Format("expr{0:X2}", this.allocadedBy.Offset); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <remarks> The tail of the list is the top of the stack </remarks>
|
||||||
|
public class CilStack: List<CilStackSlot> { |
||||||
|
public static CilStack Empty = new CilStack(); |
||||||
|
|
||||||
|
public CilStack Clone() |
||||||
|
{ |
||||||
|
return new CilStack(this); |
||||||
|
} |
||||||
|
|
||||||
|
public void PopCount(int count) |
||||||
|
{ |
||||||
|
this.RemoveRange(this.Count - count, count); |
||||||
|
} |
||||||
|
|
||||||
|
public void Push(CilStackSlot slot) |
||||||
|
{ |
||||||
|
this.Add(slot); |
||||||
|
} |
||||||
|
|
||||||
|
public CilStack(): base() |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public CilStack(IEnumerable<CilStackSlot> slotEnum): base(slotEnum) |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public override string ToString() |
||||||
|
{ |
||||||
|
string ret = "Stack: {"; |
||||||
|
bool first = true; |
||||||
|
foreach(CilStackSlot slot in this) { |
||||||
|
if (!first) ret += ", "; |
||||||
|
ret += slot.ToString(); |
||||||
|
first = false; |
||||||
|
} |
||||||
|
ret += "}"; |
||||||
|
return ret; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class StackAnalysis { |
||||||
|
MethodDefinition methodDef; |
||||||
|
Dictionary<Instruction, CilStack> stackBefore = new Dictionary<Instruction, CilStack>(); |
||||||
|
Dictionary<Instruction, CilStack> stackAfter = new Dictionary<Instruction, CilStack>(); |
||||||
|
|
||||||
|
public Dictionary<Instruction, CilStack> StackBefore { |
||||||
|
get { return stackBefore; } |
||||||
|
} |
||||||
|
|
||||||
|
public Dictionary<Instruction, CilStack> StackAfter { |
||||||
|
get { return stackAfter; } |
||||||
|
} |
||||||
|
|
||||||
|
public StackAnalysis(MethodDefinition methodDef) { |
||||||
|
this.methodDef = methodDef; |
||||||
|
|
||||||
|
foreach(Instruction inst in methodDef.Body.Instructions) { |
||||||
|
stackBefore[inst] = null; |
||||||
|
stackAfter[inst] = null; |
||||||
|
} |
||||||
|
|
||||||
|
if (methodDef.Body.Instructions.Count > 0) { |
||||||
|
Instruction firstInst = methodDef.Body.Instructions[0]; |
||||||
|
stackBefore[firstInst] = CilStack.Empty; |
||||||
|
ProcessInstructionRec(firstInst); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ProcessInstructionRec(Instruction inst) |
||||||
|
{ |
||||||
|
stackAfter[inst] = ChangeStack(stackBefore[inst], inst); |
||||||
|
|
||||||
|
switch(inst.OpCode.FlowControl) { |
||||||
|
case FlowControl.Branch: |
||||||
|
CopyStack(inst, ((Instruction)inst.Operand)); |
||||||
|
break; |
||||||
|
case FlowControl.Cond_Branch: |
||||||
|
CopyStack(inst, inst.Next); |
||||||
|
CopyStack(inst, ((Instruction)inst.Operand)); |
||||||
|
break; |
||||||
|
case FlowControl.Next: |
||||||
|
case FlowControl.Call: |
||||||
|
CopyStack(inst, inst.Next); |
||||||
|
break; |
||||||
|
case FlowControl.Return: |
||||||
|
if (stackAfter[inst].Count > 0) throw new Exception("Non-empty stack at the end"); |
||||||
|
break; |
||||||
|
default: throw new NotImplementedException(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
CilStack ChangeStack(CilStack oldStack, Instruction inst) |
||||||
|
{ |
||||||
|
CilStack newStack = oldStack.Clone(); |
||||||
|
newStack.PopCount(Util.GetNumberOfInputs(methodDef, inst)); |
||||||
|
for (int i = 0; i < Util.GetNumberOfOutputs(methodDef, inst); i++) { |
||||||
|
newStack.Push(new CilStackSlot(inst)); |
||||||
|
} |
||||||
|
return newStack; |
||||||
|
} |
||||||
|
|
||||||
|
void CopyStack(Instruction instFrom, Instruction instTo) |
||||||
|
{ |
||||||
|
CilStack mergedStack; |
||||||
|
if (!Merge(stackAfter[instFrom], stackBefore[instTo], out mergedStack)) { |
||||||
|
stackBefore[instTo] = mergedStack; |
||||||
|
ProcessInstructionRec(instTo); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool Merge(CilStack stack1, CilStack stack2, out CilStack merged) |
||||||
|
{ |
||||||
|
// Both null
|
||||||
|
if (stack1 == null && stack2 == null) { |
||||||
|
throw new Exception("Both stacks are null"); |
||||||
|
} |
||||||
|
// One of stacks null, one is not
|
||||||
|
if (stack1 == null || stack2 == null) { |
||||||
|
merged = stack1 ?? stack2; |
||||||
|
return false; |
||||||
|
} |
||||||
|
// Both are non-null
|
||||||
|
if (stack1.Count != stack2.Count) { |
||||||
|
throw new Exception("Stack merge error: different sizes"); |
||||||
|
} |
||||||
|
|
||||||
|
bool same = true; |
||||||
|
int count = stack1.Count; |
||||||
|
merged = stack1.Clone(); |
||||||
|
for (int i = 0; i < count; i++) { |
||||||
|
if (!stack1[i].Equals(stack2[i])) { |
||||||
|
merged[i] = new CilStackSlot(null); // Merge slots
|
||||||
|
same = false; |
||||||
|
} |
||||||
|
} |
||||||
|
return same; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,88 @@ |
|||||||
|
using System; |
||||||
|
|
||||||
|
using Ast = ICSharpCode.NRefactory.Ast; |
||||||
|
using ICSharpCode.NRefactory.Ast; |
||||||
|
|
||||||
|
using Cecil = Mono.Cecil; |
||||||
|
using Mono.Cecil; |
||||||
|
using Mono.Cecil.Cil; |
||||||
|
|
||||||
|
namespace Decompiler |
||||||
|
{ |
||||||
|
public static class Util |
||||||
|
{ |
||||||
|
public static int GetNumberOfInputs(MethodDefinition methodDef, Instruction inst) |
||||||
|
{ |
||||||
|
switch(inst.OpCode.StackBehaviourPop) { |
||||||
|
case StackBehaviour.Pop0: return 0; |
||||||
|
case StackBehaviour.Pop1: return 1; |
||||||
|
case StackBehaviour.Popi: return 1; |
||||||
|
case StackBehaviour.Popref: return 1; |
||||||
|
case StackBehaviour.Pop1_pop1: return 2; |
||||||
|
case StackBehaviour.Popi_pop1: return 2; |
||||||
|
case StackBehaviour.Popi_popi: return 2; |
||||||
|
case StackBehaviour.Popi_popi8: return 2; |
||||||
|
case StackBehaviour.Popi_popr4: return 2; |
||||||
|
case StackBehaviour.Popi_popr8: return 2; |
||||||
|
case StackBehaviour.Popref_pop1: return 2; |
||||||
|
case StackBehaviour.Popref_popi: return 2; |
||||||
|
case StackBehaviour.Popi_popi_popi: return 3; |
||||||
|
case StackBehaviour.Popref_popi_popi: return 3; |
||||||
|
case StackBehaviour.Popref_popi_popi8: return 3; |
||||||
|
case StackBehaviour.Popref_popi_popr4: return 3; |
||||||
|
case StackBehaviour.Popref_popi_popr8: return 3; |
||||||
|
case StackBehaviour.Popref_popi_popref: return 3; |
||||||
|
case StackBehaviour.PopAll: throw new Exception("PopAll"); |
||||||
|
case StackBehaviour.Varpop: |
||||||
|
switch(inst.OpCode.Code) { |
||||||
|
case Code.Call: |
||||||
|
Cecil.MethodReference cecilMethod = ((MethodReference)inst.Operand); |
||||||
|
if (cecilMethod.HasThis) { |
||||||
|
return cecilMethod.Parameters.Count + 1 /* this */; |
||||||
|
} else { |
||||||
|
return cecilMethod.Parameters.Count; |
||||||
|
} |
||||||
|
case Code.Calli: throw new NotImplementedException(); |
||||||
|
case Code.Callvirt: throw new NotImplementedException(); |
||||||
|
case Code.Ret: |
||||||
|
if (methodDef.ReturnType.ReturnType.FullName == Constants.Void) { |
||||||
|
return 0; |
||||||
|
} else { |
||||||
|
return 1; |
||||||
|
} |
||||||
|
case Code.Newobj: throw new NotImplementedException(); |
||||||
|
default: throw new Exception("Unknown Varpop opcode"); |
||||||
|
} |
||||||
|
default: throw new Exception("Unknown pop behaviour: " + inst.OpCode.StackBehaviourPop); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static int GetNumberOfOutputs(MethodDefinition methodDef, Instruction inst) |
||||||
|
{ |
||||||
|
switch(inst.OpCode.StackBehaviourPush) { |
||||||
|
case StackBehaviour.Push0: return 0; |
||||||
|
case StackBehaviour.Push1: return 1; |
||||||
|
case StackBehaviour.Push1_push1: return 2; |
||||||
|
case StackBehaviour.Pushi: return 1; |
||||||
|
case StackBehaviour.Pushi8: return 1; |
||||||
|
case StackBehaviour.Pushr4: return 1; |
||||||
|
case StackBehaviour.Pushr8: return 1; |
||||||
|
case StackBehaviour.Pushref: return 1; |
||||||
|
case StackBehaviour.Varpush: // Happens only for calls
|
||||||
|
switch(inst.OpCode.Code) { |
||||||
|
case Code.Call: |
||||||
|
Cecil.MethodReference cecilMethod = ((MethodReference)inst.Operand); |
||||||
|
if (cecilMethod.ReturnType.ReturnType.FullName == Constants.Void) { |
||||||
|
return 0; |
||||||
|
} else { |
||||||
|
return 1; |
||||||
|
} |
||||||
|
case Code.Calli: throw new NotImplementedException(); |
||||||
|
case Code.Callvirt: throw new NotImplementedException(); |
||||||
|
default: throw new Exception("Unknown Varpush opcode"); |
||||||
|
} |
||||||
|
default: throw new Exception("Unknown push behaviour: " + inst.OpCode.StackBehaviourPush); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue