mirror of https://github.com/icsharpcode/ILSpy.git
7 changed files with 234 additions and 244 deletions
@ -0,0 +1,71 @@
@@ -0,0 +1,71 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
using Cecil = Mono.Cecil; |
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace Decompiler |
||||
{ |
||||
public partial class ByteCode |
||||
{ |
||||
CilStack stackBefore; |
||||
CilStack stackAfter; |
||||
List<ByteCode> branchesHere = new List<ByteCode>(); |
||||
|
||||
public CilStack StackBefore { |
||||
get { return stackBefore; } |
||||
} |
||||
|
||||
public CilStack StackAfter { |
||||
get { return stackAfter; } |
||||
} |
||||
|
||||
public List<ByteCode> BranchesHere { |
||||
get { return branchesHere; } |
||||
} |
||||
|
||||
public void MergeStackBeforeWith(CilStack stack) |
||||
{ |
||||
CilStack mergedStack; |
||||
if (CilStack.Merge(this.stackBefore, stack, out mergedStack)) { |
||||
// Stacks are identical
|
||||
return; |
||||
} |
||||
this.stackBefore = mergedStack; |
||||
|
||||
stackAfter = SimulateEffectOnStack(this.StackBefore); |
||||
|
||||
switch(this.OpCode.FlowControl) { |
||||
case FlowControl.Branch: |
||||
this.BranchTarget.MergeStackBeforeWith(this.StackAfter); |
||||
break; |
||||
case FlowControl.Cond_Branch: |
||||
this.Next.MergeStackBeforeWith(this.StackAfter); |
||||
this.BranchTarget.MergeStackBeforeWith(this.StackAfter); |
||||
break; |
||||
case FlowControl.Next: |
||||
case FlowControl.Call: |
||||
this.Next.MergeStackBeforeWith(this.StackAfter); |
||||
break; |
||||
case FlowControl.Return: |
||||
if (this.StackAfter.Count > 0) throw new Exception("Non-empty stack at return instruction"); |
||||
break; |
||||
default: throw new NotImplementedException(); |
||||
} |
||||
} |
||||
|
||||
public CilStack SimulateEffectOnStack(CilStack oldStack) |
||||
{ |
||||
CilStack newStack = oldStack.Clone(); |
||||
List<Cecil.TypeReference> typeArgs = new List<Cecil.TypeReference>(); |
||||
foreach(CilStackSlot slot in newStack.PopCount(this.PopCount)) { |
||||
typeArgs.Add(slot.Type); |
||||
} |
||||
for (int i = 0; i < this.PushCount; i++) { |
||||
newStack.Push(new CilStackSlot(this, this.GetType(typeArgs.ToArray()))); |
||||
} |
||||
return newStack; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,135 @@
@@ -0,0 +1,135 @@
|
||||
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 { |
||||
ByteCode allocadedBy; |
||||
Cecil.TypeReference type; |
||||
|
||||
public ByteCode AllocadedBy { |
||||
get { return allocadedBy; } |
||||
} |
||||
|
||||
public Cecil.TypeReference Type { |
||||
get { return type; } |
||||
} |
||||
|
||||
public CilStackSlot(ByteCode allocadedBy, Cecil.TypeReference type) |
||||
{ |
||||
this.allocadedBy = allocadedBy; |
||||
this.type = type; |
||||
} |
||||
|
||||
public override int GetHashCode() |
||||
{ |
||||
int hashCode = 0; |
||||
if (allocadedBy != null) hashCode ^= allocadedBy.GetHashCode(); |
||||
if (type != null) hashCode ^= type.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) && object.Equals(this.type, myCilStackSlot.type); |
||||
} |
||||
|
||||
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 CilStackSlot[] PopCount(int count) |
||||
{ |
||||
CilStackSlot[] poped = this.GetRange(this.Count - count, count).ToArray(); |
||||
this.RemoveRange(this.Count - count, count); |
||||
return poped; |
||||
} |
||||
|
||||
public void Push(CilStackSlot slot) |
||||
{ |
||||
this.Add(slot); |
||||
} |
||||
|
||||
public CilStackSlot Peek(int depth) |
||||
{ |
||||
return this[this.Count - depth]; |
||||
} |
||||
|
||||
public CilStack(): base() |
||||
{ |
||||
|
||||
} |
||||
|
||||
public CilStack(IEnumerable<CilStackSlot> slotEnum): base(slotEnum) |
||||
{ |
||||
|
||||
} |
||||
|
||||
// Return true if stacks are same
|
||||
public static 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, null); // Merge slots
|
||||
same = false; |
||||
} |
||||
} |
||||
return same; |
||||
} |
||||
|
||||
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; |
||||
} |
||||
} |
||||
} |
@ -1,230 +0,0 @@
@@ -1,230 +0,0 @@
|
||||
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 { |
||||
ByteCode allocadedBy; |
||||
Cecil.TypeReference type; |
||||
|
||||
public ByteCode AllocadedBy { |
||||
get { return allocadedBy; } |
||||
} |
||||
|
||||
public Cecil.TypeReference Type { |
||||
get { return type; } |
||||
} |
||||
|
||||
public CilStackSlot(ByteCode allocadedBy, Cecil.TypeReference type) |
||||
{ |
||||
this.allocadedBy = allocadedBy; |
||||
this.type = type; |
||||
} |
||||
|
||||
public override int GetHashCode() |
||||
{ |
||||
int hashCode = 0; |
||||
if (allocadedBy != null) hashCode ^= allocadedBy.GetHashCode(); |
||||
if (type != null) hashCode ^= type.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) && object.Equals(this.type, myCilStackSlot.type); |
||||
} |
||||
|
||||
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 CilStackSlot[] PopCount(int count) |
||||
{ |
||||
CilStackSlot[] poped = this.GetRange(this.Count - count, count).ToArray(); |
||||
this.RemoveRange(this.Count - count, count); |
||||
return poped; |
||||
} |
||||
|
||||
public void Push(CilStackSlot slot) |
||||
{ |
||||
this.Add(slot); |
||||
} |
||||
|
||||
public CilStackSlot Peek(int depth) |
||||
{ |
||||
return this[this.Count - depth]; |
||||
} |
||||
|
||||
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 partial class StackAnalysis { |
||||
MethodDefinition methodDef; |
||||
Dictionary<ByteCode, CilStack> stackBefore = new Dictionary<ByteCode, CilStack>(); |
||||
Dictionary<ByteCode, CilStack> stackAfter = new Dictionary<ByteCode, CilStack>(); |
||||
Dictionary<ByteCode, List<ByteCode>> branchTargetOf = new Dictionary<ByteCode, List<ByteCode>>(); |
||||
|
||||
public Dictionary<ByteCode, CilStack> StackBefore { |
||||
get { return stackBefore; } |
||||
} |
||||
|
||||
public Dictionary<ByteCode, CilStack> StackAfter { |
||||
get { return stackAfter; } |
||||
} |
||||
|
||||
public Dictionary<ByteCode, List<ByteCode>> BranchTargetOf { |
||||
get { return branchTargetOf; } |
||||
} |
||||
|
||||
public Cecil.TypeReference GetTypeOf(ByteCode byteCode) |
||||
{ |
||||
if (byteCode.PushCount == 0) { |
||||
return ByteCode.TypeVoid; |
||||
} else { |
||||
return StackAfter[byteCode].Peek(1).Type; |
||||
} |
||||
} |
||||
|
||||
public StackAnalysis(MethodDefinition methodDef, ByteCodeCollection byteCodeCol) { |
||||
this.methodDef = methodDef; |
||||
|
||||
foreach(ByteCode byteCode in byteCodeCol) { |
||||
stackBefore[byteCode] = null; |
||||
stackAfter[byteCode] = null; |
||||
branchTargetOf[byteCode] = new List<ByteCode>(); |
||||
} |
||||
|
||||
foreach(ByteCode byteCode in byteCodeCol) { |
||||
if (byteCode.CanBranch) { |
||||
branchTargetOf[byteCode.BranchTarget].Add(byteCode); |
||||
} |
||||
} |
||||
|
||||
if (byteCodeCol.Count > 0) { |
||||
ByteCode firstInst = byteCodeCol[0]; |
||||
stackBefore[firstInst] = CilStack.Empty; |
||||
ProcessByteCodeRec(firstInst); |
||||
} |
||||
} |
||||
|
||||
void ProcessByteCodeRec(ByteCode byteCode) |
||||
{ |
||||
stackAfter[byteCode] = ChangeStack(stackBefore[byteCode], byteCode); |
||||
|
||||
switch(byteCode.OpCode.FlowControl) { |
||||
case FlowControl.Branch: |
||||
CopyStack(byteCode, byteCode.BranchTarget); |
||||
break; |
||||
case FlowControl.Cond_Branch: |
||||
CopyStack(byteCode, byteCode.Next); |
||||
CopyStack(byteCode, byteCode.BranchTarget); |
||||
break; |
||||
case FlowControl.Next: |
||||
case FlowControl.Call: |
||||
CopyStack(byteCode, byteCode.Next); |
||||
break; |
||||
case FlowControl.Return: |
||||
if (stackAfter[byteCode].Count > 0) throw new Exception("Non-empty stack at the end"); |
||||
break; |
||||
default: throw new NotImplementedException(); |
||||
} |
||||
} |
||||
|
||||
CilStack ChangeStack(CilStack oldStack, ByteCode byteCode) |
||||
{ |
||||
CilStack newStack = oldStack.Clone(); |
||||
CilStackSlot[] popedSlots = newStack.PopCount(byteCode.PopCount); |
||||
List<Cecil.TypeReference> typeArgs = new List<Cecil.TypeReference>(); |
||||
foreach(CilStackSlot slot in popedSlots) { |
||||
typeArgs.Add(slot.Type); |
||||
} |
||||
for (int i = 0; i < byteCode.PushCount; i++) { |
||||
newStack.Push(new CilStackSlot(byteCode, byteCode.GetType(typeArgs.ToArray()))); |
||||
} |
||||
return newStack; |
||||
} |
||||
|
||||
void CopyStack(ByteCode byteCodeFrom, ByteCode byteCodeTo) |
||||
{ |
||||
CilStack mergedStack; |
||||
if (!Merge(stackAfter[byteCodeFrom], stackBefore[byteCodeTo], out mergedStack)) { |
||||
stackBefore[byteCodeTo] = mergedStack; |
||||
ProcessByteCodeRec(byteCodeTo); |
||||
} |
||||
} |
||||
|
||||
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, null); // Merge slots
|
||||
same = false; |
||||
} |
||||
} |
||||
return same; |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue