Browse Source

Support for try-catch blocks

pull/1/head^2
David Srbecký 15 years ago
parent
commit
69753642eb
  1. 70
      src/Ast/AstMetodBodyBuilder.cs
  2. 112
      src/ILAst/ControlFlow/Nodes.cs
  3. 338
      src/ILAst/ILAstBuilder.cs
  4. 61
      src/ILAst/ILAstTypes.cs
  5. 22
      src/Mono.Cecil.Rocks/MyRocks.cs

70
src/Ast/AstMetodBodyBuilder.cs

@ -3,12 +3,10 @@ using System.Collections.Generic; @@ -3,12 +3,10 @@ using System.Collections.Generic;
using Ast = ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Ast;
using Cecil = Mono.Cecil;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using Decompiler.ControlFlow;
namespace Decompiler
@ -26,7 +24,9 @@ namespace Decompiler @@ -26,7 +24,9 @@ namespace Decompiler
try {
return builder.CreateMetodBody();
} catch {
return new BlockStatement();
BlockStatement block = new BlockStatement();
block.Children.Add(MakeComment("Exception during decompilation"));
return block;
}
}
@ -38,7 +38,7 @@ namespace Decompiler @@ -38,7 +38,7 @@ namespace Decompiler
methodDef.Body.SimplifyMacros();
List<ILExpression> body = ILAstBuilder.Build(methodDef);
List<ILNode> body = new ILAstBuilder().Build(methodDef);
MethodBodyGraph bodyGraph = new MethodBodyGraph(body);
bodyGraph.Optimize();
@ -108,10 +108,14 @@ namespace Decompiler @@ -108,10 +108,14 @@ namespace Decompiler
yield return new Ast.LabelStatement(node.Label);
if (node is BasicBlock) {
foreach(ILExpression expr in ((BasicBlock)node).Body) {
Statement stmt = TransformExpressionToStatement(expr);
if (stmt != null) {
yield return stmt;
foreach(ILNode expr in ((BasicBlock)node).Body) {
if (expr is ILLabel) {
yield return new Ast.LabelStatement(((ILLabel)expr).Name);
} else {
Statement stmt = TransformExpressionToStatement((ILExpression)expr);
if (stmt != null) {
yield return stmt;
}
}
}
Node fallThroughNode = ((BasicBlock)node).FallThroughBasicBlock;
@ -185,6 +189,28 @@ namespace Decompiler @@ -185,6 +189,28 @@ namespace Decompiler
falseBlock.Parent = ifElseStmt;
yield return ifElseStmt;
} else if (node is TryCatchNode) {
TryCatchNode tryCachNode = ((TryCatchNode)node);
Ast.BlockStatement tryBlock = new Ast.BlockStatement();
tryBlock.Children.AddRange(TransformNode(tryCachNode.Childs[0]));
Ast.BlockStatement finallyBlock = null;
if (tryCachNode.Childs[1].Childs.Count > 0) {
finallyBlock = new Ast.BlockStatement();
finallyBlock.Children.AddRange(TransformNode(tryCachNode.Childs[1]));
}
List<Ast.CatchClause> ccs = new List<CatchClause>();
for (int i = 0; i < tryCachNode.Types.Count; i++) {
Ast.BlockStatement catchBlock = new Ast.BlockStatement();
catchBlock.Children.AddRange(TransformNode(tryCachNode.Childs[i + 2]));
Ast.CatchClause cc = new Ast.CatchClause(
new Ast.TypeReference(tryCachNode.Types[i].FullName),
"exception",
catchBlock
);
ccs.Add(cc);
}
Ast.TryCatchStatement tryCachStmt = new Ast.TryCatchStatement(tryBlock, ccs, finallyBlock);
yield return tryCachStmt;
} else {
throw new Exception("Bad node type");
}
@ -238,10 +264,10 @@ namespace Decompiler @@ -238,10 +264,10 @@ namespace Decompiler
Ast.Expression MakeBranchCondition_Internal(Branch branch)
{
if (branch is SimpleBranch) {
List<Ast.Expression> args = TransformExpressionArguments(((SimpleBranch)branch).BasicBlock.Body[0]);
List<Ast.Expression> args = TransformExpressionArguments((ILExpression)((SimpleBranch)branch).BasicBlock.Body[0]);
Ast.Expression arg1 = args.Count >= 1 ? args[0] : null;
Ast.Expression arg2 = args.Count >= 2 ? args[1] : null;
switch(((SimpleBranch)branch).BasicBlock.Body[0].OpCode.Code) {
switch(((ILExpression)((SimpleBranch)branch).BasicBlock.Body[0]).OpCode.Code) {
case Code.Brfalse: return new Ast.UnaryOperatorExpression(arg1, UnaryOperatorType.Not);
case Code.Brtrue: return arg1;
case Code.Beq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2);
@ -349,7 +375,7 @@ namespace Decompiler @@ -349,7 +375,7 @@ namespace Decompiler
Ast.Statement branchCommand = null;
if (byteCode.Operand is ILExpression) {
branchCommand = new Ast.GotoStatement(((ILExpression)byteCode.Operand).BasicBlock.Label);
branchCommand = new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name);
}
switch(opCode.Code) {
@ -546,10 +572,10 @@ namespace Decompiler @@ -546,10 +572,10 @@ namespace Decompiler
case Code.Cpobj: throw new NotImplementedException();
case Code.Dup: return arg1;
case Code.Endfilter: throw new NotImplementedException();
case Code.Endfinally: throw new NotImplementedException();
case Code.Endfinally: return null;
case Code.Initblk: throw new NotImplementedException();
case Code.Initobj: throw new NotImplementedException();
case Code.Isinst: throw new NotImplementedException();
case Code.Isinst: return new Ast.TypeOfIsExpression(arg1, new Ast.TypeReference(((Cecil.TypeReference)operand).FullName));
case Code.Jmp: throw new NotImplementedException();
case Code.Ldarg:
if (methodDef.HasThis && ((ParameterDefinition)operand).Index == 0) {
@ -602,7 +628,12 @@ namespace Decompiler @@ -602,7 +628,12 @@ namespace Decompiler
case Code.Ldflda:
case Code.Ldsflda: throw new NotImplementedException();
case Code.Ldftn: throw new NotImplementedException();
case Code.Ldloc: return new Ast.IdentifierExpression(((VariableDefinition)operand).Name);
case Code.Ldloc:
if (operand is ILStackVariable) {
return new Ast.IdentifierExpression(((ILStackVariable)operand).Name);
} else {
return new Ast.IdentifierExpression(((VariableDefinition)operand).Name);
}
case Code.Ldloca: throw new NotImplementedException();
case Code.Ldnull: return new Ast.PrimitiveExpression(null, null);
case Code.Ldobj: throw new NotImplementedException();
@ -617,7 +648,7 @@ namespace Decompiler @@ -617,7 +648,7 @@ namespace Decompiler
throw new NotImplementedException();
}
case Code.Ldvirtftn: throw new NotImplementedException();
case Code.Leave: throw new NotImplementedException();
case Code.Leave: return null;
case Code.Localloc: throw new NotImplementedException();
case Code.Mkrefany: throw new NotImplementedException();
case Code.Newobj:
@ -636,7 +667,7 @@ namespace Decompiler @@ -636,7 +667,7 @@ namespace Decompiler
case Code.No: throw new NotImplementedException();
case Code.Nop: return null;
case Code.Or: throw new NotImplementedException();
case Code.Pop: throw new NotImplementedException();
case Code.Pop: return arg1;
case Code.Readonly: throw new NotImplementedException();
case Code.Refanytype: throw new NotImplementedException();
case Code.Refanyval: throw new NotImplementedException();
@ -648,10 +679,13 @@ namespace Decompiler @@ -648,10 +679,13 @@ namespace Decompiler
return new Ast.ReturnStatement(null);
}
}
case Code.Rethrow: throw new NotImplementedException();
case Code.Rethrow: return new Ast.ThrowStatement(new IdentifierExpression("exception"));
case Code.Sizeof: throw new NotImplementedException();
case Code.Starg: throw new NotImplementedException();
case Code.Stloc: {
if (operand is ILStackVariable) {
return new Ast.AssignmentExpression(new Ast.IdentifierExpression(((ILStackVariable)operand).Name), AssignmentOperatorType.Assign, arg1);
}
VariableDefinition locVar = (VariableDefinition)operand;
string name = locVar.Name;
arg1 = Convert(arg1, locVar.VariableType);
@ -671,7 +705,7 @@ namespace Decompiler @@ -671,7 +705,7 @@ namespace Decompiler
case Code.Stobj: throw new NotImplementedException();
case Code.Switch: throw new NotImplementedException();
case Code.Tail: throw new NotImplementedException();
case Code.Throw: throw new NotImplementedException();
case Code.Throw: return new Ast.ThrowStatement(arg1);
case Code.Unaligned: throw new NotImplementedException();
case Code.Unbox: throw new NotImplementedException();
case Code.Unbox_Any: throw new NotImplementedException();

112
src/ILAst/ControlFlow/Nodes.cs

@ -1,18 +1,20 @@ @@ -1,18 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Decompiler.Mono.Cecil.Rocks;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace Decompiler.ControlFlow
{
public class BasicBlock: Node
{
List<ILExpression> body = new List<ILExpression>();
List<ILNode> body = new List<ILNode>();
List<BasicBlock> basicBlockPredecessors = new List<BasicBlock>();
BasicBlock fallThroughBasicBlock;
BasicBlock branchBasicBlock;
public List<ILExpression> Body {
public List<ILNode> Body {
get { return body; }
}
@ -117,48 +119,98 @@ namespace Decompiler.ControlFlow @@ -117,48 +119,98 @@ namespace Decompiler.ControlFlow
get { return methodEntry; }
}
public MethodBodyGraph(List<ILExpression> exprs)
Dictionary<ILLabel, BasicBlock> labelToBasicBlock = new Dictionary<ILLabel, BasicBlock>();
public MethodBodyGraph(List<ILNode> ast)
{
if (ast.Count == 0) throw new ArgumentException("Count == 0", "ast");
this.methodEntry = new BasicBlock();
this.Childs.Add(this.methodEntry);
this.Childs.AddRange(SplitToBasicBlocks(ast));
// Add branch links to BasicBlocks
foreach(BasicBlock basicBlock in this.BasicBlocks) {
foreach(ILNode node in basicBlock.Body) {
if (node is ILExpression) {
ILExpression expr = (ILExpression)node;
if (expr.Operand is ILLabel) {
BasicBlock target = labelToBasicBlock[(ILLabel)expr.Operand];
basicBlock.BranchBasicBlock = target;
target.BasicBlockPredecessors.Add(basicBlock);
}
// TODO: Switch
}
}
}
}
public List<Node> SplitToBasicBlocks(List<ILNode> ast)
{
if (exprs.Count == 0) throw new ArgumentException("Count == 0", "exprs");
if (ast.Count == 0) return new List<Node>();
List<Node> nodes = new List<Node>();
BasicBlock basicBlock = null;
for(int i = 0; i < exprs.Count; i++) {
for(int i = 0; i < ast.Count; i++) {
if (i == 0 ||
exprs[i - 1].OpCode.IsBranch() ||
exprs[i].IsBranchTarget ||
exprs[i].OpCode.IsBranch())
ast[i] is ILLabel ||
ast[i - 1] is ILTryCatchBlock ||
ast[i] is ILTryCatchBlock ||
(ast[i - 1] is ILExpression) && ((ILExpression)ast[i - 1]).OpCode.IsBranch() ||
(ast[i] is ILExpression) && ((ILExpression)ast[i]).OpCode.IsBranch())
{
BasicBlock oldBB = basicBlock;
basicBlock = new BasicBlock();
this.Childs.Add(basicBlock);
nodes.Add(basicBlock);
// Links
if (oldBB != null && ast[i - 1] is ILExpression && ((ILExpression)ast[i - 1]).OpCode.CanFallThough()) {
oldBB.FallThroughBasicBlock = basicBlock;
basicBlock.BasicBlockPredecessors.Add(oldBB);
}
}
if (ast[i] is ILTryCatchBlock) {
nodes.Add(ConvertTryCatch((ILTryCatchBlock)ast[i]));
} else {
basicBlock.Body.Add(ast[i]);
}
if (ast[i] is ILLabel) {
labelToBasicBlock[(ILLabel)ast[i]] = basicBlock;
}
basicBlock.Body.Add(exprs[i]);
exprs[i].SetBasicBlock(basicBlock);
}
// Add fall-through links to BasicBlocks
for(int i = 0; i < exprs.Count - 1; i++) {
BasicBlock node = exprs[i].BasicBlock;
BasicBlock target = exprs[i + 1].BasicBlock;
if (target != node && exprs[i].OpCode.CanFallThough()) {
node.FallThroughBasicBlock = target;
target.BasicBlockPredecessors.Add(node);
}
return nodes;
}
public TryCatchNode ConvertTryCatch(ILTryCatchBlock ilTryCatch)
{
TryCatchNode tryCatch = new TryCatchNode();
Block tryBlock = new Block();
tryBlock.Childs.AddRange(SplitToBasicBlocks(ilTryCatch.TryBlock));
tryBlock.MoveTo(tryCatch);
Block finallyBlock = new Block();
if (ilTryCatch.FinallyBlock != null) {
finallyBlock.Childs.AddRange(SplitToBasicBlocks(ilTryCatch.FinallyBlock));
}
finallyBlock.MoveTo(tryCatch);
// Add branch links to BasicBlocks
for(int i = 0; i < exprs.Count; i++) {
if (exprs[i].OpCode.IsBranch()) {
BasicBlock node = exprs[i].BasicBlock;
BasicBlock target = ((ILExpression)exprs[i].Operand).BasicBlock;
node.BranchBasicBlock = target;
target.BasicBlockPredecessors.Add(node);
}
foreach(ILTryCatchBlock.CatchBlock cb in ilTryCatch.CatchBlocks) {
tryCatch.Types.Add(cb.ExceptionType);
Block catchBlock = new Block();
catchBlock.Childs.AddRange(SplitToBasicBlocks(cb.Body));
catchBlock.MoveTo(tryCatch);
}
this.methodEntry = (BasicBlock)this.HeadChild;
return tryCatch;
}
}
public class TryCatchNode: Node
{
public List<TypeReference> Types = new List<TypeReference>();
}
public class AcyclicGraph: Node

338
src/ILAst/ILAstBuilder.cs

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Decompiler.Mono.Cecil.Rocks;
using Mono.Cecil;
using Mono.Cecil.Cil;
@ -11,29 +11,16 @@ namespace Decompiler @@ -11,29 +11,16 @@ 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 Instruction PushedBy;
public TypeReference Type;
public Slot(ByteCode byteCode, TypeReference type)
public Slot(Instruction inst, TypeReference type)
{
this.PushedBy = byteCode;
this.PushedBy = inst;
this.Type = type;
}
}
@ -49,167 +36,234 @@ namespace Decompiler @@ -49,167 +36,234 @@ namespace Decompiler
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"));
sb.Append(s.PushedBy.Offset.ToString("X"));
first = false;
}
return sb.ToString();
}
}
public static List<ILExpression> Build(MethodDefinition methodDef)
Dictionary<Instruction, ILStack> stackBefore = new Dictionary<Instruction, ILAstBuilder.ILStack>();
Dictionary<Instruction, ILLabel> labels = new Dictionary<Instruction, ILLabel>();
public List<ILNode> Build(MethodDefinition methodDef)
{
List<ByteCode> body = new List<ByteCode>();
List<Instruction> body = new List<Instruction>(methodDef.Body.Instructions);
if (body.Count == 0) return new List<ILNode>();
Dictionary<Instruction, ByteCode> instToByteCode = new Dictionary<Instruction, ByteCode>();
StackAnalysis(body, methodDef);
// 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);
// Create branch labels for instructins; use the labels as branch operands
foreach (Instruction inst in body) {
if (inst.Operand is Instruction[]) {
List<ILLabel> newOperand = new List<ILLabel>();
foreach(Instruction target in (Instruction[])inst.Operand) {
if (!labels.ContainsKey(target)) {
labels[target] = new ILLabel() { Name = "IL_" + target.Offset.ToString("X2") };
}
newOperand.Add(labels[target]);
}
inst.Operand = newOperand.ToArray();
} 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") };
}
inst.Operand = labels[target];
}
}
// Stack analisys
if (body.Count > 0) {
Queue<ByteCode> agenda = new Queue<ByteCode>();
// Add known states
body[0].StackBefore = new ILStack();
agenda.Enqueue(body[0]);
List<ILNode> ast = ConvertToAst(body, methodDef.Body.ExceptionHandlers);
return ast;
}
public void StackAnalysis(List<Instruction> body, MethodDefinition methodDef)
{
Queue<Instruction> agenda = new Queue<Instruction>();
// Add known states
stackBefore[body[0]] = new ILStack();
agenda.Enqueue(body[0]);
if(methodDef.Body.HasExceptionHandlers) {
foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) {
stackBefore[ex.TryStart] = new ILStack();
agenda.Enqueue(ex.TryStart);
ILStack stack = new ILStack();
stack.Items.Add(new ILStack.Slot(null, MyRocks.TypeException));
stackBefore[ex.HandlerStart] = stack;
agenda.Enqueue(ex.HandlerStart);
}
}
// Process agenda
while(agenda.Count > 0) {
Instruction inst = agenda.Dequeue();
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);
}
// 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));
}
// 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
List<Instruction> branchTargets = new List<Instruction>();
if (inst.OpCode.CanFallThough()) {
branchTargets.Add(inst.Next);
}
if (inst.OpCode.IsBranch()) {
if (inst.Operand is Instruction[]) {
branchTargets.AddRange((Instruction[])inst.Operand);
} else {
branchTargets.Add((Instruction)inst.Operand);
}
// 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);
}
foreach (Instruction branchTarget in branchTargets) {
ILStack nextStack;
if (stackBefore.TryGetValue(branchTarget, out nextStack)) {
// TODO: Compare stacks
} else {
stackBefore[branchTarget] = newStack;
agenda.Enqueue(branchTarget);
}
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);
}
}
}
}
}
public List<ILNode> ConvertToAst(List<Instruction> body, IEnumerable<ExceptionHandler> ehs)
{
List<ILNode> ast = new List<ILNode>();
while (ehs.Any()) {
ILTryCatchBlock tryCatchBlock = new ILTryCatchBlock();
// Find the first and widest scope
int tryStart = ehs.Min(eh => eh.TryStart.Offset);
int tryEnd = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.Offset);
var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).ToList();
// Cut all instructions up to the try block
{
int tryStartIdx;
for (tryStartIdx = 0; body[tryStartIdx].Offset != tryStart; tryStartIdx++);
ast.AddRange(ConvertToAst(body.CutRange(0, tryStartIdx)));
}
// Cut the try block
{
List<ExceptionHandler> nestedEHs = ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd)).ToList();
int tryEndIdx;
for (tryEndIdx = 0; tryEndIdx < body.Count && body[tryEndIdx].Offset != tryEnd; tryEndIdx++);
tryCatchBlock.TryBlock = ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs);
}
// Cut all handlers
tryCatchBlock.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>();
foreach(ExceptionHandler eh in handlers) {
int start;
for (start = 0; body[start] != eh.HandlerStart; start++);
int end;
for (end = 0; body[end] != eh.HandlerEnd; 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);
if (eh.HandlerType == ExceptionHandlerType.Catch) {
tryCatchBlock.CatchBlocks.Add(new ILTryCatchBlock.CatchBlock() {
ExceptionType = eh.CatchType,
Body = handlerAst
});
} else if (eh.HandlerType == ExceptionHandlerType.Finally) {
tryCatchBlock.FinallyBlock = handlerAst;
} else {
// TODO
}
}
ehs = ehs.Where(eh => eh.TryStart.Offset > tryEnd).ToList();
ast.Add(tryCatchBlock);
}
List<ILExpression> ast = new List<ILExpression>();
Dictionary<ByteCode, ILExpression> byteCodeToExpr = new Dictionary<ByteCode, ILExpression>();
// Add whatever is left
ast.AddRange(ConvertToAst(body));
return ast;
}
public List<ILNode> ConvertToAst(List<Instruction> body)
{
List<ILNode> ast = new List<ILNode>();
// Convert stack-based IL code to ILAst tree
foreach(ByteCode byteCode in body) {
ILExpression expr = new ILExpression(byteCode.Instruction.OpCode, byteCode.Instruction.Operand);
foreach(Instruction inst in body) {
ILExpression expr = new ILExpression(inst.OpCode, inst.Operand);
byteCodeToExpr[byteCode] = expr;
// Label for this instruction
ILLabel label;
if (labels.TryGetValue(inst, out label)) {
ast.Add(label);
}
// 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);
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") });
expr.Arguments.Add(ldExpr);
} else {
ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, new ILStackVariable() { Name = "exception" });
expr.Arguments.Add(ldExpr);
}
}
// If the bytecode pushes anything store the result in temporary variable
int pushCount = byteCode.Instruction.GetPushCount();
int pushCount = inst.GetPushCount();
if (pushCount > 0) {
ILExpression stExpr = new ILExpression(OpCodes.Stloc, new VariableDefinition(byteCode.TmpVarName, null));
ILExpression stExpr = new ILExpression(OpCodes.Stloc, new ILStackVariable() { Name = "expr" + inst.Offset.ToString("X2"), RefCount = pushCount });
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];
ILExpression expr = ast[i] as ILExpression;
ILExpression nextExpr = ast[i + 1] as ILExpression;
if (expr.IsTempStloc && !nextExpr.IsBranchTarget) {
if (expr != null && nextExpr != null && expr.OpCode.Code == Code.Stloc && expr.Operand is ILStackVariable) {
// If the next expression is stloc, look inside
if (nextExpr.IsTempStloc) {
if (nextExpr.OpCode.Code == Code.Stloc && nextExpr.Operand is ILStackVariable) {
nextExpr = nextExpr.Arguments[0];
}
@ -217,24 +271,22 @@ namespace Decompiler @@ -217,24 +271,22 @@ namespace Decompiler
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) {
// 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
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
}
} else {
break; // This argument might have side effects so we can not move the 'expr' after it.
}
}
}

61
src/ILAst/ILAstTypes.cs

@ -9,19 +9,54 @@ using Cecil = Mono.Cecil; @@ -9,19 +9,54 @@ using Cecil = Mono.Cecil;
namespace Decompiler
{
public class ILExpression
public class ILNode
{
}
public class ILLabel: ILNode
{
public string Name;
public override string ToString()
{
return Name + ":";
}
}
public class ILTryCatchBlock: ILNode
{
public class CatchBlock
{
public TypeReference ExceptionType;
public List<ILNode> Body;
}
public List<ILNode> TryBlock;
public List<CatchBlock> CatchBlocks;
public List<ILNode> FinallyBlock;
public override string ToString()
{
return "Try-Catch{}";
}
}
public class ILStackVariable
{
public string Name;
public int RefCount;
public override string ToString()
{
return Name;
}
}
public class ILExpression: ILNode
{
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)
{
@ -30,14 +65,6 @@ namespace Decompiler @@ -30,14 +65,6 @@ namespace Decompiler
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();

22
src/Mono.Cecil.Rocks/MyRocks.cs

@ -24,6 +24,16 @@ namespace Decompiler.Mono.Cecil.Rocks @@ -24,6 +24,16 @@ namespace Decompiler.Mono.Cecil.Rocks
static public TypeReference TypeZero = GetCecilType(typeof(Int32));
static public TypeReference TypeOne = GetCecilType(typeof(Int32));
public static List<T> CutRange<T>(this List<T> list, int start, int count)
{
List<T> ret = new List<T>(count);
for (int i = 0; i < count; i++) {
ret.Add(list[start + i]);
}
list.RemoveRange(start, count);
return ret;
}
public static bool CanFallThough(this OpCode opCode)
{
switch(opCode.FlowControl) {
@ -33,6 +43,7 @@ namespace Decompiler.Mono.Cecil.Rocks @@ -33,6 +43,7 @@ namespace Decompiler.Mono.Cecil.Rocks
case FlowControl.Call: return true;
case FlowControl.Return: return false;
case FlowControl.Throw: return false;
case FlowControl.Meta: return false;
default: throw new NotImplementedException();
}
}
@ -42,7 +53,7 @@ namespace Decompiler.Mono.Cecil.Rocks @@ -42,7 +53,7 @@ namespace Decompiler.Mono.Cecil.Rocks
return opCode.FlowControl == FlowControl.Branch || opCode.FlowControl == FlowControl.Cond_Branch;
}
public static int GetPopCount(this Instruction inst, MethodDefinition methodDef, int stackLength)
public static int GetPopCount(this Instruction inst)
{
switch(inst.OpCode.StackBehaviourPop) {
case StackBehaviour.Pop0: return 0;
@ -63,7 +74,7 @@ namespace Decompiler.Mono.Cecil.Rocks @@ -63,7 +74,7 @@ namespace Decompiler.Mono.Cecil.Rocks
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 stackLength;
case StackBehaviour.PopAll: return int.MaxValue;
case StackBehaviour.Varpop:
switch(inst.OpCode.Code) {
case Code.Call:
@ -75,12 +86,7 @@ namespace Decompiler.Mono.Cecil.Rocks @@ -75,12 +86,7 @@ namespace Decompiler.Mono.Cecil.Rocks
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.Ret: return int.MaxValue;
case Code.Newobj:
MethodReference ctorMethod = ((MethodReference)inst.Operand);
return ctorMethod.Parameters.Count;

Loading…
Cancel
Save