mirror of https://github.com/icsharpcode/ILSpy.git
31 changed files with 765 additions and 1203 deletions
@ -1,83 +0,0 @@
@@ -1,83 +0,0 @@
|
||||
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); |
||||
|
||||
if (this.OpCode.Code == Code.Endfinally) { |
||||
return; // TODO
|
||||
} |
||||
|
||||
if (this.OpCode.Code == Code.Switch) { |
||||
this.Next.MergeStackBeforeWith(this.StackAfter); |
||||
foreach(ByteCode target in (ByteCode[])this.Operand) { |
||||
target.MergeStackBeforeWith(this.StackAfter); |
||||
} |
||||
return; |
||||
} |
||||
|
||||
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; |
||||
} |
||||
} |
||||
} |
@ -1,100 +0,0 @@
@@ -1,100 +0,0 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
using Cecil = Mono.Cecil; |
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace Decompiler |
||||
{ |
||||
public partial class ByteCode |
||||
{ |
||||
public int PopCount { |
||||
get { |
||||
return GetPopCount(); |
||||
} |
||||
} |
||||
|
||||
public int PushCount { |
||||
get { |
||||
return GetPushCount(); |
||||
} |
||||
} |
||||
|
||||
int GetPopCount() |
||||
{ |
||||
switch(this.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: return this.StackBefore.Count; |
||||
case StackBehaviour.Varpop: |
||||
switch(this.OpCode.Code) { |
||||
case Code.Call: |
||||
case Code.Callvirt: |
||||
Cecil.MethodReference cecilMethod = ((MethodReference)this.Operand); |
||||
if (cecilMethod.HasThis) { |
||||
return cecilMethod.Parameters.Count + 1 /* this */; |
||||
} else { |
||||
return cecilMethod.Parameters.Count; |
||||
} |
||||
case Code.Calli: throw new NotImplementedException(); |
||||
case Code.Ret: |
||||
if (methodDef.ReturnType.FullName == Constants.Void) { |
||||
return 0; |
||||
} else { |
||||
return 1; |
||||
} |
||||
case Code.Newobj: |
||||
Cecil.MethodReference ctorMethod = ((MethodReference)this.Operand); |
||||
return ctorMethod.Parameters.Count; |
||||
default: throw new Exception("Unknown Varpop opcode"); |
||||
} |
||||
default: throw new Exception("Unknown pop behaviour: " + this.OpCode.StackBehaviourPop); |
||||
} |
||||
} |
||||
|
||||
int GetPushCount() |
||||
{ |
||||
switch(this.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(this.OpCode.Code) { |
||||
case Code.Call: |
||||
case Code.Callvirt: |
||||
Cecil.MethodReference cecilMethod = ((MethodReference)this.Operand); |
||||
if (cecilMethod.ReturnType.FullName == Constants.Void) { |
||||
return 0; |
||||
} else { |
||||
return 1; |
||||
} |
||||
case Code.Calli: throw new NotImplementedException(); |
||||
default: throw new Exception("Unknown Varpush opcode"); |
||||
} |
||||
default: throw new Exception("Unknown push behaviour: " + this.OpCode.StackBehaviourPush); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,116 +0,0 @@
@@ -1,116 +0,0 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
using Cecil = Mono.Cecil; |
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace Decompiler |
||||
{ |
||||
public partial class ByteCode |
||||
{ |
||||
ByteCode previous; |
||||
ByteCode next; |
||||
|
||||
MethodDefinition methodDef; |
||||
int offset; |
||||
OpCode opCode; |
||||
object operand; |
||||
|
||||
public ByteCode Previous { |
||||
get { return previous; } |
||||
set { previous = value; } |
||||
} |
||||
|
||||
public ByteCode Next { |
||||
get { return next; } |
||||
set { next = value; } |
||||
} |
||||
|
||||
public int Offset { |
||||
get { return offset; } |
||||
set { offset = value; } |
||||
} |
||||
|
||||
public OpCode OpCode { |
||||
get { return opCode; } |
||||
set { opCode = value; } |
||||
} |
||||
|
||||
public object Operand { |
||||
get { return operand; } |
||||
set { operand = value; } |
||||
} |
||||
|
||||
public ByteCode BranchTarget { |
||||
get { |
||||
return operand as ByteCode; |
||||
} |
||||
} |
||||
|
||||
public bool CanBranch { |
||||
get { |
||||
return OpCode.FlowControl == FlowControl.Branch || |
||||
OpCode.FlowControl == FlowControl.Cond_Branch; |
||||
} |
||||
} |
||||
|
||||
public ByteCode(MethodDefinition methodDef, Instruction inst) |
||||
{ |
||||
this.methodDef = methodDef; |
||||
this.offset = inst.Offset; |
||||
this.opCode = inst.OpCode; |
||||
this.operand = inst.Operand; |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
return this.OpCode + " " + FormatByteCodeOperand(this.Operand); |
||||
} |
||||
|
||||
public string Description { |
||||
get { |
||||
return string.Format( |
||||
"{1, -22} # {2}->{3} {4} {5}", |
||||
this.Offset, |
||||
this.OpCode + " " + FormatByteCodeOperand(this.Operand), |
||||
this.OpCode.StackBehaviourPop, |
||||
this.OpCode.StackBehaviourPush, |
||||
this.OpCode.FlowControl == FlowControl.Next ? string.Empty : "Flow=" + opCode.FlowControl, |
||||
this.OpCode.OpCodeType == OpCodeType.Macro ? "(macro)" : string.Empty |
||||
); |
||||
} |
||||
} |
||||
|
||||
public string FormatedOperand { |
||||
get { |
||||
return FormatByteCodeOperand(this.Operand); |
||||
} |
||||
} |
||||
|
||||
public static string FormatByteCodeOperand(object operand) |
||||
{ |
||||
if (operand == null) { |
||||
return string.Empty; |
||||
} else if (operand is ByteCode) { |
||||
return string.Format("IL_{0:X2}", ((ByteCode)operand).Offset); |
||||
} else if (operand is MethodReference) { |
||||
return ((MethodReference)operand).Name + "()"; |
||||
} else if (operand is Cecil.TypeReference) { |
||||
return ((Cecil.TypeReference)operand).FullName; |
||||
} else if (operand is VariableDefinition) { |
||||
return ((VariableDefinition)operand).Name; |
||||
} else if (operand is ParameterDefinition) { |
||||
return ((ParameterDefinition)operand).Name; |
||||
} else if (operand is FieldReference) { |
||||
return ((FieldReference)operand).Name; |
||||
} else if (operand is string) { |
||||
return "\"" + operand + "\""; |
||||
} else if (operand is int) { |
||||
return operand.ToString(); |
||||
} else { |
||||
return operand.ToString(); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,100 +0,0 @@
@@ -1,100 +0,0 @@
|
||||
using System; |
||||
using System.Collections; |
||||
using System.Collections.Generic; |
||||
|
||||
using Cecil = Mono.Cecil; |
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace Decompiler |
||||
{ |
||||
public class ByteCodeCollection: IEnumerable<ByteCode> |
||||
{ |
||||
List<ByteCode> list = new List<ByteCode>(); |
||||
|
||||
public IEnumerator<ByteCode> GetEnumerator() |
||||
{ |
||||
return list.GetEnumerator(); |
||||
} |
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() |
||||
{ |
||||
return list.GetEnumerator(); |
||||
} |
||||
|
||||
public int Count { |
||||
get { |
||||
return list.Count; |
||||
} |
||||
} |
||||
|
||||
public ByteCode this[int index] { |
||||
get { |
||||
return list[index]; |
||||
} |
||||
} |
||||
|
||||
public ByteCode GetByOffset(int offset) |
||||
{ |
||||
foreach(ByteCode byteCode in this) { |
||||
if (byteCode.Offset == offset) { |
||||
return byteCode; |
||||
} |
||||
} |
||||
throw new Exception("Not found"); |
||||
} |
||||
|
||||
public int IndexOf(ByteCode byteCode) |
||||
{ |
||||
return list.IndexOf(byteCode); |
||||
} |
||||
|
||||
public ByteCodeCollection(MethodDefinition methodDef) |
||||
{ |
||||
foreach(Instruction inst in methodDef.Body.Instructions) { |
||||
list.Add(new ByteCode(methodDef, inst)); |
||||
} |
||||
foreach(ByteCode byteCode in this) { |
||||
if (byteCode.CanBranch) { |
||||
if (byteCode.Operand is Instruction[]) { |
||||
List<ByteCode> operands = new List<ByteCode>(); |
||||
foreach(Instruction inst in (Instruction[])byteCode.Operand) { |
||||
ByteCode target = GetByOffset(inst.Offset); |
||||
operands.Add(target); |
||||
target.BranchesHere.Add(byteCode); |
||||
} |
||||
byteCode.Operand = operands.ToArray(); |
||||
} else { |
||||
ByteCode target = GetByOffset(((Instruction)byteCode.Operand).Offset); |
||||
byteCode.Operand = target; |
||||
target.BranchesHere.Add(byteCode); |
||||
} |
||||
} |
||||
} |
||||
UpdateNextPrevious(); |
||||
UpdateStackAnalysis(methodDef); |
||||
} |
||||
|
||||
void UpdateNextPrevious() |
||||
{ |
||||
for(int i = 0; i < list.Count - 1; i++) { |
||||
this[i].Next = this[i + 1]; |
||||
this[i + 1].Previous = this[i]; |
||||
} |
||||
} |
||||
|
||||
void UpdateStackAnalysis(MethodDefinition methodDef) |
||||
{ |
||||
if (this.Count > 0) { |
||||
this[0].MergeStackBeforeWith(CilStack.Empty); |
||||
} |
||||
foreach(ExceptionHandler handler in methodDef.Body.ExceptionHandlers) { |
||||
ByteCode byteCode = this.GetByOffset(handler.HandlerStart.Offset); |
||||
CilStack expetionStack = new CilStack(); |
||||
// TODO: Allocated by what?
|
||||
expetionStack.Add(new CilStackSlot(byteCode, ByteCode.TypeException)); |
||||
byteCode.MergeStackBeforeWith(expetionStack); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,137 +0,0 @@
@@ -1,137 +0,0 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
using Cecil = Mono.Cecil; |
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace Decompiler |
||||
{ |
||||
public class ByteCodeExpression |
||||
{ |
||||
ControlFlow.BasicBlock basicBlock; |
||||
|
||||
OpCode opCode; |
||||
object operand; |
||||
List<ByteCodeExpression> arguments = new List<ByteCodeExpression>(); |
||||
bool returnsValue; |
||||
Cecil.TypeReference type; |
||||
|
||||
ByteCodeExpression branchTarget; |
||||
List<ByteCodeExpression> branchesHere = new List<ByteCodeExpression>(); |
||||
|
||||
bool isSSASR = false; |
||||
|
||||
public Decompiler.ControlFlow.BasicBlock BasicBlock { |
||||
get { return basicBlock; } |
||||
set { |
||||
basicBlock = value; |
||||
foreach (ByteCodeExpression argument in arguments) { |
||||
argument.BasicBlock = value; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public OpCode OpCode { |
||||
get { return opCode; } |
||||
set { opCode = value; } |
||||
} |
||||
|
||||
public object Operand { |
||||
get { return operand; } |
||||
set { operand = value; } |
||||
} |
||||
|
||||
public List<ByteCodeExpression> Arguments { |
||||
get { return arguments; } |
||||
} |
||||
|
||||
public bool ReturnsValue { |
||||
get { return returnsValue; } |
||||
set { returnsValue = value; } |
||||
} |
||||
|
||||
public TypeReference Type { |
||||
get { return type; } |
||||
set { type = value; } |
||||
} |
||||
|
||||
/// <summary> Single static assignment; single read </summary>
|
||||
public bool IsSSASR { |
||||
get { return isSSASR; } |
||||
set { isSSASR = value; } |
||||
} |
||||
|
||||
public ByteCodeExpression BranchTarget { |
||||
get { return branchTarget; } |
||||
set { branchTarget = value; } |
||||
} |
||||
|
||||
public List<ByteCodeExpression> BranchesHere { |
||||
get { return branchesHere; } |
||||
} |
||||
|
||||
public bool IsBranchTarget { |
||||
get { return BranchesHere.Count > 0; } |
||||
} |
||||
|
||||
public static ByteCodeExpression Ldloc(string name) |
||||
{ |
||||
return new ByteCodeExpression(OpCodes.Ldloc, new VariableDefinition(name, null), true); |
||||
} |
||||
|
||||
public static ByteCodeExpression Stloc(string name) |
||||
{ |
||||
return new ByteCodeExpression(OpCodes.Stloc, new VariableDefinition(name, null), false); |
||||
} |
||||
|
||||
public ByteCodeExpression(OpCode opCode, object operand, bool returnsValue) |
||||
{ |
||||
this.opCode = opCode; |
||||
this.operand = operand; |
||||
this.returnsValue = returnsValue; |
||||
} |
||||
|
||||
public ByteCodeExpression(ByteCode byteCode) |
||||
{ |
||||
this.OpCode = byteCode.OpCode; |
||||
this.Operand = byteCode.Operand; |
||||
foreach(CilStackSlot arg in byteCode.StackBefore.PeekCount(byteCode.PopCount)) { |
||||
string name = string.Format("expr{0:X2}", arg.AllocadedBy.Offset); |
||||
this.Arguments.Add(Ldloc(name)); |
||||
} |
||||
this.ReturnsValue = byteCode.PushCount > 0; |
||||
this.Type = byteCode.Type; |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
return string.Format("[ByteCodeExpression OpCode={0}]", this.opCode); |
||||
} |
||||
|
||||
public ByteCodeExpression Clone() |
||||
{ |
||||
ByteCodeExpression clone = (ByteCodeExpression)this.MemberwiseClone(); |
||||
clone.branchTarget = null; |
||||
clone.branchesHere = new List<ByteCodeExpression>(); |
||||
return clone; |
||||
} |
||||
|
||||
public bool IsConstant() |
||||
{ |
||||
if (!IsOpCodeConstant()) return false; |
||||
foreach(ByteCodeExpression arg in this.Arguments) { |
||||
if (!arg.IsConstant()) return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
bool IsOpCodeConstant() |
||||
{ |
||||
switch(this.OpCode.Code) { |
||||
case Code.Ldarg: return true; |
||||
default: return false; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,108 +0,0 @@
@@ -1,108 +0,0 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
using Cecil = Mono.Cecil; |
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace Decompiler |
||||
{ |
||||
public class ByteCodeExpressionCollection: List<ByteCodeExpression> |
||||
{ |
||||
public ByteCodeExpressionCollection(ByteCodeCollection byteCodeCol) |
||||
{ |
||||
Dictionary<ByteCode, ByteCodeExpression> exprForByteCode = new Dictionary<ByteCode, ByteCodeExpression>(); |
||||
|
||||
foreach(ByteCode byteCode in byteCodeCol) { |
||||
ByteCodeExpression newExpr = new ByteCodeExpression(byteCode); |
||||
|
||||
// If the bytecode pushes anything encapsulate it with stloc
|
||||
if (byteCode.PushCount > 0) { |
||||
string name = string.Format("expr{0:X2}", byteCode.Offset); |
||||
ByteCodeExpression stExpr = ByteCodeExpression.Stloc(name); |
||||
stExpr.Arguments.Add(newExpr); |
||||
stExpr.IsSSASR = byteCode.PushCount == 1; |
||||
newExpr = stExpr; |
||||
} |
||||
|
||||
exprForByteCode[byteCode] = newExpr; |
||||
this.Add(newExpr); |
||||
} |
||||
|
||||
// Branching links
|
||||
foreach(ByteCodeExpression expr in this) { |
||||
if (expr.Operand is ByteCode) { |
||||
expr.BranchTarget = exprForByteCode[(ByteCode)expr.Operand]; |
||||
expr.BranchTarget.BranchesHere.Add(expr); |
||||
} |
||||
} |
||||
} |
||||
|
||||
Dictionary<ByteCodeExpression, object> alreadyDuplicated = new Dictionary<ByteCodeExpression, object>(); |
||||
|
||||
public void Optimize() |
||||
{ |
||||
for(int i = 0; i < this.Count - 1; i++) { |
||||
if (i < 0) continue; |
||||
|
||||
ByteCodeExpression expr = this[i]; |
||||
ByteCodeExpression nextExpr = this[i + 1]; |
||||
|
||||
// Duplicate 'dup' expressions
|
||||
|
||||
if (expr.OpCode.Code == Code.Stloc && |
||||
expr.Arguments[0].OpCode.Code == Code.Dup && |
||||
expr.Arguments[0].Arguments[0].IsConstant() && |
||||
!alreadyDuplicated.ContainsKey(expr)) |
||||
{ |
||||
Options.NotifyCollapsingExpression(); |
||||
this.Insert(i + 1, expr.Clone()); |
||||
this[i].IsSSASR = true; |
||||
this[i + 1].IsSSASR = true; |
||||
alreadyDuplicated.Add(this[i], null); |
||||
alreadyDuplicated.Add(this[i + 1], null); |
||||
continue; |
||||
} |
||||
|
||||
// Try to in-line stloc into following expression
|
||||
|
||||
if (expr.OpCode.Code == Code.Stloc && |
||||
expr.IsSSASR && |
||||
!nextExpr.IsBranchTarget) { |
||||
|
||||
// If the next expression is stloc, look inside
|
||||
if (nextExpr.OpCode.Code == Code.Stloc && |
||||
nextExpr.Arguments[0].OpCode.Code != Code.Ldloc) { |
||||
nextExpr = nextExpr.Arguments[0]; |
||||
} |
||||
|
||||
// Find the use of the 'expr'
|
||||
for(int j = 0; j < nextExpr.Arguments.Count; j++) { |
||||
ByteCodeExpression arg = nextExpr.Arguments[j]; |
||||
|
||||
if (arg.OpCode.Code == Code.Ldloc && |
||||
((VariableDefinition)arg.Operand).Name == ((VariableDefinition)expr.Operand).Name) { |
||||
// Found
|
||||
Options.NotifyCollapsingExpression(); |
||||
this.RemoveAt(i); i--; // Remove the stloc
|
||||
nextExpr.Arguments[j] = expr.Arguments[0]; // Inline the stloc body
|
||||
// Move branch links
|
||||
foreach(ByteCodeExpression predExpr in expr.BranchesHere) { |
||||
predExpr.BranchTarget = this[i + 1]; |
||||
predExpr.BranchTarget.BranchesHere.Add(predExpr); |
||||
} |
||||
|
||||
i--; // Try the same index again
|
||||
break; |
||||
} |
||||
if (arg.OpCode.Code != Code.Ldloc) { |
||||
// This argument might have side effects so we can not
|
||||
// move the 'expr' after it. Terminate
|
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,144 +0,0 @@
@@ -1,144 +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) |
||||
{ |
||||
if (allocadedBy == null) throw new ArgumentNullException(); |
||||
if (type == null) throw new ArgumentNullException(); |
||||
|
||||
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[] PeekCount(int count) |
||||
{ |
||||
return this.GetRange(this.Count - count, count).ToArray(); |
||||
} |
||||
|
||||
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])) { |
||||
// TODO: Do the merge
|
||||
// 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; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,246 @@
@@ -0,0 +1,246 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Text; |
||||
|
||||
using Decompiler.Mono.Cecil.Rocks; |
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
using Cecil = Mono.Cecil; |
||||
|
||||
namespace Decompiler |
||||
{ |
||||
public class ILAstBuilder |
||||
{ |
||||
class ByteCode |
||||
{ |
||||
public Instruction Instruction; |
||||
public ILStack StackBefore; |
||||
|
||||
public string TmpVarName { get { return "expr" + this.Instruction.Offset; } } |
||||
|
||||
public override string ToString() |
||||
{ |
||||
return string.Format("[{0}, [{1}]]", Instruction, StackBefore); |
||||
} |
||||
} |
||||
|
||||
class ILStack |
||||
{ |
||||
public class Slot |
||||
{ |
||||
public ByteCode PushedBy; |
||||
public TypeReference Type; |
||||
|
||||
public Slot(ByteCode byteCode, TypeReference type) |
||||
{ |
||||
this.PushedBy = byteCode; |
||||
this.Type = type; |
||||
} |
||||
} |
||||
|
||||
public List<Slot> Items = new List<Slot>(); |
||||
|
||||
public ILStack Clone() |
||||
{ |
||||
ILStack clone = new ILStack(); |
||||
foreach(Slot s in this.Items) { |
||||
clone.Items.Add(new Slot(s.PushedBy, s.Type)); |
||||
} |
||||
return clone; |
||||
} |
||||
|
||||
public void MergeInto(ref ILStack other, out bool changed) |
||||
{ |
||||
if (other == null) { |
||||
other = this; |
||||
changed = true; |
||||
} else { |
||||
changed = false; |
||||
} |
||||
} |
||||
|
||||
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.Instruction.Offset.ToString("X")); |
||||
first = false; |
||||
} |
||||
return sb.ToString(); |
||||
} |
||||
} |
||||
|
||||
public static List<ILExpression> Build(MethodDefinition methodDef) |
||||
{ |
||||
List<ByteCode> body = new List<ByteCode>(); |
||||
|
||||
Dictionary<Instruction, ByteCode> instToByteCode = new Dictionary<Instruction, ByteCode>(); |
||||
|
||||
// Create the IL body for analisys
|
||||
foreach(Instruction inst in methodDef.Body.Instructions) { |
||||
ByteCode byteCode = new ByteCode(); |
||||
byteCode.Instruction = inst; |
||||
body.Add(byteCode); |
||||
instToByteCode.Add(inst, byteCode); |
||||
} |
||||
|
||||
// Stack analisys
|
||||
if (body.Count > 0) { |
||||
Queue<ByteCode> agenda = new Queue<ByteCode>(); |
||||
|
||||
// Add known states
|
||||
body[0].StackBefore = new ILStack(); |
||||
agenda.Enqueue(body[0]); |
||||
|
||||
if(methodDef.Body.HasExceptionHandlers) { |
||||
foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) { |
||||
ByteCode tryStart = instToByteCode[ex.TryStart]; |
||||
tryStart.StackBefore = new ILStack(); |
||||
agenda.Enqueue(tryStart); |
||||
|
||||
ByteCode handlerStart = instToByteCode[ex.HandlerStart]; |
||||
handlerStart.StackBefore = new ILStack(); |
||||
handlerStart.StackBefore.Items.Add(new ILStack.Slot(null, MyRocks.TypeException)); |
||||
agenda.Enqueue(handlerStart); |
||||
} |
||||
} |
||||
|
||||
// Process agenda
|
||||
while(agenda.Count > 0) { |
||||
ByteCode byteCode = agenda.Dequeue(); |
||||
|
||||
// What is the effect of the instruction on the stack?
|
||||
ILStack newStack = byteCode.StackBefore.Clone(); |
||||
int popCount = byteCode.Instruction.GetPopCount(methodDef, byteCode.StackBefore.Items.Count); |
||||
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 = byteCode.Instruction.GetTypeInternal(methodDef, typeArgs); |
||||
} catch { |
||||
type = MyRocks.TypeObject; |
||||
} |
||||
if (popCount > 0) { |
||||
newStack.Items.RemoveRange(newStack.Items.Count - popCount, popCount); |
||||
} |
||||
int pushCount = byteCode.Instruction.GetPushCount(); |
||||
for (int i = 0; i < pushCount; i++) { |
||||
newStack.Items.Add(new ILStack.Slot(byteCode, type)); |
||||
} |
||||
|
||||
// Apply the state to any successors
|
||||
if (byteCode.Instruction.OpCode.CanFallThough()) { |
||||
ByteCode next = instToByteCode[byteCode.Instruction.Next]; |
||||
bool changed; |
||||
newStack.MergeInto(ref next.StackBefore, out changed); |
||||
if (changed) agenda.Enqueue(next); |
||||
} |
||||
if (byteCode.Instruction.OpCode.IsBranch()) { |
||||
object operand = byteCode.Instruction.Operand; |
||||
if (operand is Instruction) { |
||||
ByteCode next = instToByteCode[(Instruction)operand]; |
||||
bool changed; |
||||
newStack.MergeInto(ref next.StackBefore, out changed); |
||||
if (changed) agenda.Enqueue(next); |
||||
} else { |
||||
foreach(Instruction inst in (Instruction[])operand) { |
||||
ByteCode next = instToByteCode[inst]; |
||||
bool changed; |
||||
newStack.MergeInto(ref next.StackBefore, out changed); |
||||
if (changed) agenda.Enqueue(next); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
List<ILExpression> ast = new List<ILExpression>(); |
||||
Dictionary<ByteCode, ILExpression> byteCodeToExpr = new Dictionary<ByteCode, ILExpression>(); |
||||
|
||||
// Convert stack-based IL code to ILAst tree
|
||||
foreach(ByteCode byteCode in body) { |
||||
ILExpression expr = new ILExpression(byteCode.Instruction.OpCode, byteCode.Instruction.Operand); |
||||
|
||||
byteCodeToExpr[byteCode] = expr; |
||||
|
||||
// Reference arguments using temporary variables
|
||||
ILStack stack = byteCode.StackBefore; |
||||
for (int i = stack.Items.Count - byteCode.Instruction.GetPopCount(methodDef, byteCode.StackBefore.Items.Count); i < stack.Items.Count; i++) { |
||||
ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, new VariableDefinition(stack.Items[i].PushedBy.TmpVarName, null)); |
||||
ldExpr.IsTempLdloc = true; |
||||
expr.Arguments.Add(ldExpr); |
||||
} |
||||
|
||||
// If the bytecode pushes anything store the result in temporary variable
|
||||
int pushCount = byteCode.Instruction.GetPushCount(); |
||||
if (pushCount > 0) { |
||||
ILExpression stExpr = new ILExpression(OpCodes.Stloc, new VariableDefinition(byteCode.TmpVarName, null)); |
||||
stExpr.Arguments.Add(expr); |
||||
stExpr.IsTempStloc = true; |
||||
stExpr.RefCount = pushCount; |
||||
expr.Partent = stExpr; |
||||
expr = stExpr; |
||||
} |
||||
|
||||
ast.Add(expr); |
||||
} |
||||
|
||||
// Convert branch operands
|
||||
foreach(ILExpression expr in ast) { |
||||
if (expr.Operand is Instruction) { |
||||
ILExpression brTarget = byteCodeToExpr[instToByteCode[(Instruction)expr.Operand]]; |
||||
expr.Operand = brTarget; |
||||
brTarget.IsBranchTarget = true; |
||||
if (brTarget.Partent != null) { |
||||
brTarget.Partent.IsBranchTarget = true; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Try to in-line stloc / ldloc pairs
|
||||
for(int i = 0; i < ast.Count - 1; i++) { |
||||
ILExpression expr = ast[i]; |
||||
ILExpression nextExpr = ast[i + 1]; |
||||
|
||||
if (expr.IsTempStloc && !nextExpr.IsBranchTarget) { |
||||
|
||||
// If the next expression is stloc, look inside
|
||||
if (nextExpr.IsTempStloc) { |
||||
nextExpr = nextExpr.Arguments[0]; |
||||
} |
||||
|
||||
// Find the use of the 'expr'
|
||||
for(int j = 0; j < nextExpr.Arguments.Count; j++) { |
||||
ILExpression arg = nextExpr.Arguments[j]; |
||||
|
||||
if (!arg.IsTempLdloc) { |
||||
break; // This argument might have side effects so we can not move the 'expr' after it.
|
||||
} else { |
||||
if (((VariableDefinition)arg.Operand).Name == ((VariableDefinition)expr.Operand).Name) { |
||||
expr.RefCount--; |
||||
if (expr.RefCount <= 0) { |
||||
ast.RemoveAt(i); |
||||
} |
||||
nextExpr.Arguments[j] = expr.Arguments[0]; // Inline the stloc body
|
||||
if (expr.IsBranchTarget) { |
||||
nextExpr.IsBranchTarget = true; |
||||
if (nextExpr.Partent != null) { |
||||
nextExpr.Partent.IsBranchTarget = true; |
||||
} |
||||
} |
||||
i = Math.Max(0, i - 2); // Try the same index again
|
||||
break; // Found
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
return ast; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,60 @@
@@ -0,0 +1,60 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Text; |
||||
|
||||
using Decompiler.ControlFlow; |
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
using Cecil = Mono.Cecil; |
||||
|
||||
namespace Decompiler |
||||
{ |
||||
public class ILExpression |
||||
{ |
||||
public OpCode OpCode { get; set; } |
||||
public object Operand { get; set; } |
||||
public List<ILExpression> Arguments { get; set; } |
||||
public bool IsTempStloc { get; set; } |
||||
public bool IsTempLdloc { get; set; } |
||||
public int RefCount { get; set; } |
||||
public bool IsBranchTarget { get; set; } |
||||
public BasicBlock BasicBlock { get; set; } |
||||
|
||||
// HACK: Do preoperly
|
||||
public ILExpression Partent { get; set; } |
||||
|
||||
public ILExpression(OpCode opCode, object operand, params ILExpression[] args) |
||||
{ |
||||
this.OpCode = opCode; |
||||
this.Operand = operand; |
||||
this.Arguments = new List<ILExpression>(args); |
||||
} |
||||
|
||||
public void SetBasicBlock(BasicBlock bb) |
||||
{ |
||||
this.BasicBlock = bb; |
||||
foreach(ILExpression arg in Arguments) { |
||||
arg.SetBasicBlock(bb); |
||||
} |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
StringBuilder sb = new StringBuilder(); |
||||
sb.Append(OpCode.Name); |
||||
sb.Append('('); |
||||
bool first = true; |
||||
if (Operand != null) { |
||||
sb.Append(Operand.ToString()); |
||||
first = false; |
||||
} |
||||
foreach (ILExpression arg in this.Arguments) { |
||||
if (!first) sb.Append(","); |
||||
sb.Append(arg.ToString()); |
||||
first = false; |
||||
} |
||||
sb.Append(')'); |
||||
return sb.ToString(); |
||||
} |
||||
} |
||||
} |
@ -1,16 +0,0 @@
@@ -1,16 +0,0 @@
|
||||
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 |
||||
{ |
||||
|
||||
} |
||||
} |
Binary file not shown.
Loading…
Reference in new issue