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

112
src/ILAst/ControlFlow/Nodes.cs

@ -1,18 +1,20 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Decompiler.Mono.Cecil.Rocks; using Decompiler.Mono.Cecil.Rocks;
using Mono.Cecil;
using Mono.Cecil.Cil; using Mono.Cecil.Cil;
namespace Decompiler.ControlFlow namespace Decompiler.ControlFlow
{ {
public class BasicBlock: Node public class BasicBlock: Node
{ {
List<ILExpression> body = new List<ILExpression>(); List<ILNode> body = new List<ILNode>();
List<BasicBlock> basicBlockPredecessors = new List<BasicBlock>(); List<BasicBlock> basicBlockPredecessors = new List<BasicBlock>();
BasicBlock fallThroughBasicBlock; BasicBlock fallThroughBasicBlock;
BasicBlock branchBasicBlock; BasicBlock branchBasicBlock;
public List<ILExpression> Body { public List<ILNode> Body {
get { return body; } get { return body; }
} }
@ -117,48 +119,98 @@ namespace Decompiler.ControlFlow
get { return methodEntry; } 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; BasicBlock basicBlock = null;
for(int i = 0; i < exprs.Count; i++) {
for(int i = 0; i < ast.Count; i++) {
if (i == 0 || if (i == 0 ||
exprs[i - 1].OpCode.IsBranch() || ast[i] is ILLabel ||
exprs[i].IsBranchTarget || ast[i - 1] is ILTryCatchBlock ||
exprs[i].OpCode.IsBranch()) 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(); 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 return nodes;
for(int i = 0; i < exprs.Count - 1; i++) { }
BasicBlock node = exprs[i].BasicBlock;
BasicBlock target = exprs[i + 1].BasicBlock; public TryCatchNode ConvertTryCatch(ILTryCatchBlock ilTryCatch)
{
if (target != node && exprs[i].OpCode.CanFallThough()) { TryCatchNode tryCatch = new TryCatchNode();
node.FallThroughBasicBlock = target;
target.BasicBlockPredecessors.Add(node); 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 foreach(ILTryCatchBlock.CatchBlock cb in ilTryCatch.CatchBlocks) {
for(int i = 0; i < exprs.Count; i++) { tryCatch.Types.Add(cb.ExceptionType);
if (exprs[i].OpCode.IsBranch()) { Block catchBlock = new Block();
BasicBlock node = exprs[i].BasicBlock; catchBlock.Childs.AddRange(SplitToBasicBlocks(cb.Body));
BasicBlock target = ((ILExpression)exprs[i].Operand).BasicBlock; catchBlock.MoveTo(tryCatch);
node.BranchBasicBlock = target;
target.BasicBlockPredecessors.Add(node);
}
} }
this.methodEntry = (BasicBlock)this.HeadChild; return tryCatch;
} }
}
public class TryCatchNode: Node
{
public List<TypeReference> Types = new List<TypeReference>();
} }
public class AcyclicGraph: Node public class AcyclicGraph: Node

338
src/ILAst/ILAstBuilder.cs

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using Decompiler.Mono.Cecil.Rocks; using Decompiler.Mono.Cecil.Rocks;
using Mono.Cecil; using Mono.Cecil;
using Mono.Cecil.Cil; using Mono.Cecil.Cil;
@ -11,29 +11,16 @@ namespace Decompiler
{ {
public class ILAstBuilder 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 class ILStack
{ {
public class Slot public class Slot
{ {
public ByteCode PushedBy; public Instruction PushedBy;
public TypeReference Type; public TypeReference Type;
public Slot(ByteCode byteCode, TypeReference type) public Slot(Instruction inst, TypeReference type)
{ {
this.PushedBy = byteCode; this.PushedBy = inst;
this.Type = type; this.Type = type;
} }
} }
@ -49,167 +36,234 @@ namespace Decompiler
return clone; 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() public override string ToString()
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
bool first = true; bool first = true;
foreach (Slot s in this.Items) { foreach (Slot s in this.Items) {
if (!first) sb.Append(", "); if (!first) sb.Append(", ");
sb.Append(s.PushedBy.Instruction.Offset.ToString("X")); sb.Append(s.PushedBy.Offset.ToString("X"));
first = false; first = false;
} }
return sb.ToString(); 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 // Create branch labels for instructins; use the labels as branch operands
foreach(Instruction inst in methodDef.Body.Instructions) { foreach (Instruction inst in body) {
ByteCode byteCode = new ByteCode(); if (inst.Operand is Instruction[]) {
byteCode.Instruction = inst; List<ILLabel> newOperand = new List<ILLabel>();
body.Add(byteCode); foreach(Instruction target in (Instruction[])inst.Operand) {
instToByteCode.Add(inst, byteCode); 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 List<ILNode> ast = ConvertToAst(body, methodDef.Body.ExceptionHandlers);
if (body.Count > 0) {
Queue<ByteCode> agenda = new Queue<ByteCode>(); return ast;
}
// Add known states
body[0].StackBefore = new ILStack(); public void StackAnalysis(List<Instruction> body, MethodDefinition methodDef)
agenda.Enqueue(body[0]); {
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) { // What is the effect of the instruction on the stack?
foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) { ILStack newStack = stackBefore[inst].Clone();
ByteCode tryStart = instToByteCode[ex.TryStart]; int popCount = inst.GetPopCount();
tryStart.StackBefore = new ILStack(); if (popCount == int.MaxValue) popCount = stackBefore[inst].Items.Count; // Pop all
agenda.Enqueue(tryStart); List<TypeReference> typeArgs = new List<TypeReference>();
for (int i = newStack.Items.Count - popCount; i < newStack.Items.Count; i++) {
ByteCode handlerStart = instToByteCode[ex.HandlerStart]; typeArgs.Add(newStack.Items[i].Type);
handlerStart.StackBefore = new ILStack(); }
handlerStart.StackBefore.Items.Add(new ILStack.Slot(null, MyRocks.TypeException)); TypeReference type;
agenda.Enqueue(handlerStart); 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 // Apply the state to any successors
while(agenda.Count > 0) { List<Instruction> branchTargets = new List<Instruction>();
ByteCode byteCode = agenda.Dequeue(); if (inst.OpCode.CanFallThough()) {
branchTargets.Add(inst.Next);
// What is the effect of the instruction on the stack? }
ILStack newStack = byteCode.StackBefore.Clone(); if (inst.OpCode.IsBranch()) {
int popCount = byteCode.Instruction.GetPopCount(methodDef, byteCode.StackBefore.Items.Count); if (inst.Operand is Instruction[]) {
List<TypeReference> typeArgs = new List<TypeReference>(); branchTargets.AddRange((Instruction[])inst.Operand);
for (int i = newStack.Items.Count - popCount; i < newStack.Items.Count; i++) { } else {
typeArgs.Add(newStack.Items[i].Type); branchTargets.Add((Instruction)inst.Operand);
}
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 foreach (Instruction branchTarget in branchTargets) {
if (byteCode.Instruction.OpCode.CanFallThough()) { ILStack nextStack;
ByteCode next = instToByteCode[byteCode.Instruction.Next]; if (stackBefore.TryGetValue(branchTarget, out nextStack)) {
bool changed; // TODO: Compare stacks
newStack.MergeInto(ref next.StackBefore, out changed); } else {
if (changed) agenda.Enqueue(next); 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; public List<ILNode> ConvertToAst(List<Instruction> body, IEnumerable<ExceptionHandler> ehs)
newStack.MergeInto(ref next.StackBefore, out changed); {
if (changed) agenda.Enqueue(next); List<ILNode> ast = new List<ILNode>();
} else {
foreach(Instruction inst in (Instruction[])operand) { while (ehs.Any()) {
ByteCode next = instToByteCode[inst]; ILTryCatchBlock tryCatchBlock = new ILTryCatchBlock();
bool changed;
newStack.MergeInto(ref next.StackBefore, out changed); // Find the first and widest scope
if (changed) agenda.Enqueue(next); 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>(); // Add whatever is left
Dictionary<ByteCode, ILExpression> byteCodeToExpr = new Dictionary<ByteCode, ILExpression>(); 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 // Convert stack-based IL code to ILAst tree
foreach(ByteCode byteCode in body) { foreach(Instruction inst in body) {
ILExpression expr = new ILExpression(byteCode.Instruction.OpCode, byteCode.Instruction.Operand); 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 // Reference arguments using temporary variables
ILStack stack = byteCode.StackBefore; ILStack stack = stackBefore[inst];
for (int i = stack.Items.Count - byteCode.Instruction.GetPopCount(methodDef, byteCode.StackBefore.Items.Count); i < stack.Items.Count; i++) { int popCount = inst.GetPopCount();
ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, new VariableDefinition(stack.Items[i].PushedBy.TmpVarName, null)); if (popCount == int.MaxValue) popCount = stackBefore[inst].Items.Count; // Pop all
ldExpr.IsTempLdloc = true; for (int i = stack.Items.Count - popCount; i < stack.Items.Count; i++) {
expr.Arguments.Add(ldExpr); 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 // If the bytecode pushes anything store the result in temporary variable
int pushCount = byteCode.Instruction.GetPushCount(); int pushCount = inst.GetPushCount();
if (pushCount > 0) { 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.Arguments.Add(expr);
stExpr.IsTempStloc = true;
stExpr.RefCount = pushCount;
expr.Partent = stExpr;
expr = stExpr; expr = stExpr;
} }
ast.Add(expr); 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 // Try to in-line stloc / ldloc pairs
for(int i = 0; i < ast.Count - 1; i++) { for(int i = 0; i < ast.Count - 1; i++) {
ILExpression expr = ast[i]; ILExpression expr = ast[i] as ILExpression;
ILExpression nextExpr = ast[i + 1]; 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 the next expression is stloc, look inside
if (nextExpr.IsTempStloc) { if (nextExpr.OpCode.Code == Code.Stloc && nextExpr.Operand is ILStackVariable) {
nextExpr = nextExpr.Arguments[0]; nextExpr = nextExpr.Arguments[0];
} }
@ -217,24 +271,22 @@ namespace Decompiler
for(int j = 0; j < nextExpr.Arguments.Count; j++) { for(int j = 0; j < nextExpr.Arguments.Count; j++) {
ILExpression arg = nextExpr.Arguments[j]; ILExpression arg = nextExpr.Arguments[j];
if (!arg.IsTempLdloc) { // TODO: Check if duplicating the dup opcode has side-effects
break; // This argument might have side effects so we can not move the 'expr' after it.
} else { if (arg.OpCode.Code == Code.Ldloc && arg.Operand is ILStackVariable) {
if (((VariableDefinition)arg.Operand).Name == ((VariableDefinition)expr.Operand).Name) { ILStackVariable stVar = (ILStackVariable)expr.Operand;
expr.RefCount--; ILStackVariable ldVar = (ILStackVariable)arg.Operand;
if (expr.RefCount <= 0) { if (stVar.Name == ldVar.Name) {
stVar.RefCount--;
if (stVar.RefCount <= 0) {
ast.RemoveAt(i); ast.RemoveAt(i);
} }
nextExpr.Arguments[j] = expr.Arguments[0]; // Inline the stloc body 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 i = Math.Max(0, i - 2); // Try the same index again
break; // Found 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;
namespace Decompiler 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 OpCode OpCode { get; set; }
public object Operand { get; set; } public object Operand { get; set; }
public List<ILExpression> Arguments { 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) public ILExpression(OpCode opCode, object operand, params ILExpression[] args)
{ {
@ -30,14 +65,6 @@ namespace Decompiler
this.Arguments = new List<ILExpression>(args); 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() public override string ToString()
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();

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

@ -24,6 +24,16 @@ namespace Decompiler.Mono.Cecil.Rocks
static public TypeReference TypeZero = GetCecilType(typeof(Int32)); static public TypeReference TypeZero = GetCecilType(typeof(Int32));
static public TypeReference TypeOne = 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) public static bool CanFallThough(this OpCode opCode)
{ {
switch(opCode.FlowControl) { switch(opCode.FlowControl) {
@ -33,6 +43,7 @@ namespace Decompiler.Mono.Cecil.Rocks
case FlowControl.Call: return true; case FlowControl.Call: return true;
case FlowControl.Return: return false; case FlowControl.Return: return false;
case FlowControl.Throw: return false; case FlowControl.Throw: return false;
case FlowControl.Meta: return false;
default: throw new NotImplementedException(); default: throw new NotImplementedException();
} }
} }
@ -42,7 +53,7 @@ namespace Decompiler.Mono.Cecil.Rocks
return opCode.FlowControl == FlowControl.Branch || opCode.FlowControl == FlowControl.Cond_Branch; 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) { switch(inst.OpCode.StackBehaviourPop) {
case StackBehaviour.Pop0: return 0; case StackBehaviour.Pop0: return 0;
@ -63,7 +74,7 @@ namespace Decompiler.Mono.Cecil.Rocks
case StackBehaviour.Popref_popi_popr4: return 3; case StackBehaviour.Popref_popi_popr4: return 3;
case StackBehaviour.Popref_popi_popr8: return 3; case StackBehaviour.Popref_popi_popr8: return 3;
case StackBehaviour.Popref_popi_popref: return 3; case StackBehaviour.Popref_popi_popref: return 3;
case StackBehaviour.PopAll: return stackLength; case StackBehaviour.PopAll: return int.MaxValue;
case StackBehaviour.Varpop: case StackBehaviour.Varpop:
switch(inst.OpCode.Code) { switch(inst.OpCode.Code) {
case Code.Call: case Code.Call:
@ -75,12 +86,7 @@ namespace Decompiler.Mono.Cecil.Rocks
return cecilMethod.Parameters.Count; return cecilMethod.Parameters.Count;
} }
case Code.Calli: throw new NotImplementedException(); case Code.Calli: throw new NotImplementedException();
case Code.Ret: case Code.Ret: return int.MaxValue;
if (methodDef.ReturnType.FullName == Constants.Void) {
return 0;
} else {
return 1;
}
case Code.Newobj: case Code.Newobj:
MethodReference ctorMethod = ((MethodReference)inst.Operand); MethodReference ctorMethod = ((MethodReference)inst.Operand);
return ctorMethod.Parameters.Count; return ctorMethod.Parameters.Count;

Loading…
Cancel
Save