using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using dnlib.DotNet.Emit;
namespace Confuser.Core.Helpers {
///
/// A Control Flow Graph (CFG) of a method
///
public class ControlFlowGraph : IEnumerable {
readonly List blocks;
readonly CilBody body;
readonly int[] instrBlocks;
readonly Dictionary indexMap;
ControlFlowGraph(CilBody body) {
this.body = body;
instrBlocks = new int[body.Instructions.Count];
blocks = new List();
indexMap = new Dictionary();
for (int i = 0; i < body.Instructions.Count; i++)
indexMap.Add(body.Instructions[i], i);
}
///
/// Gets the number of blocks in this CFG.
///
/// The number of blocks.
public int Count {
get { return blocks.Count; }
}
///
/// Gets the of the specified id.
///
/// The id.
/// The block with specified id.
public ControlFlowBlock this[int id] {
get { return blocks[id]; }
}
///
/// Gets the corresponding method body.
///
/// The method body.
public CilBody Body {
get { return body; }
}
IEnumerator IEnumerable.GetEnumerator() {
return blocks.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return blocks.GetEnumerator();
}
///
/// Gets the block containing the specified instruction.
///
/// The index of instruction.
/// The block containing the instruction.
public ControlFlowBlock GetContainingBlock(int instrIndex) {
return blocks[instrBlocks[instrIndex]];
}
///
/// Gets the index of the specified instruction.
///
/// The instruction.
/// The index of instruction.
public int IndexOf(Instruction instr) {
return indexMap[instr];
}
void PopulateBlockHeaders(HashSet blockHeaders, HashSet entryHeaders) {
for (int i = 0; i < body.Instructions.Count; i++) {
Instruction instr = body.Instructions[i];
if (instr.Operand is Instruction) {
blockHeaders.Add((Instruction)instr.Operand);
if (i + 1 < body.Instructions.Count)
blockHeaders.Add(body.Instructions[i + 1]);
}
else if (instr.Operand is Instruction[]) {
foreach (Instruction target in (Instruction[])instr.Operand)
blockHeaders.Add(target);
if (i + 1 < body.Instructions.Count)
blockHeaders.Add(body.Instructions[i + 1]);
}
else if ((instr.OpCode.FlowControl == FlowControl.Throw || instr.OpCode.FlowControl == FlowControl.Return) &&
i + 1 < body.Instructions.Count) {
blockHeaders.Add(body.Instructions[i + 1]);
}
}
blockHeaders.Add(body.Instructions[0]);
foreach (ExceptionHandler eh in body.ExceptionHandlers) {
blockHeaders.Add(eh.TryStart);
blockHeaders.Add(eh.HandlerStart);
blockHeaders.Add(eh.FilterStart);
entryHeaders.Add(eh.HandlerStart);
entryHeaders.Add(eh.FilterStart);
}
}
void SplitBlocks(HashSet blockHeaders, HashSet entryHeaders) {
int nextBlockId = 0;
int currentBlockId = -1;
Instruction currentBlockHdr = null;
for (int i = 0; i < body.Instructions.Count; i++) {
Instruction instr = body.Instructions[i];
if (blockHeaders.Contains(instr)) {
if (currentBlockHdr != null) {
Instruction footer = body.Instructions[i - 1];
var type = ControlFlowBlockType.Normal;
if (entryHeaders.Contains(currentBlockHdr) || currentBlockHdr == body.Instructions[0])
type |= ControlFlowBlockType.Entry;
if (footer.OpCode.FlowControl == FlowControl.Return || footer.OpCode.FlowControl == FlowControl.Throw)
type |= ControlFlowBlockType.Exit;
blocks.Add(new ControlFlowBlock(currentBlockId, type, currentBlockHdr, footer));
}
currentBlockId = nextBlockId++;
currentBlockHdr = instr;
}
instrBlocks[i] = currentBlockId;
}
if (blocks.Count == 0 || blocks[blocks.Count - 1].Id != currentBlockId) {
Instruction footer = body.Instructions[body.Instructions.Count - 1];
var type = ControlFlowBlockType.Normal;
if (entryHeaders.Contains(currentBlockHdr) || currentBlockHdr == body.Instructions[0])
type |= ControlFlowBlockType.Entry;
if (footer.OpCode.FlowControl == FlowControl.Return || footer.OpCode.FlowControl == FlowControl.Throw)
type |= ControlFlowBlockType.Exit;
blocks.Add(new ControlFlowBlock(currentBlockId, type, currentBlockHdr, footer));
}
}
void LinkBlocks() {
for (int i = 0; i < body.Instructions.Count; i++) {
Instruction instr = body.Instructions[i];
if (instr.Operand is Instruction) {
ControlFlowBlock srcBlock = blocks[instrBlocks[i]];
ControlFlowBlock dstBlock = blocks[instrBlocks[indexMap[(Instruction)instr.Operand]]];
dstBlock.Sources.Add(srcBlock);
srcBlock.Targets.Add(dstBlock);
}
else if (instr.Operand is Instruction[]) {
foreach (Instruction target in (Instruction[])instr.Operand) {
ControlFlowBlock srcBlock = blocks[instrBlocks[i]];
ControlFlowBlock dstBlock = blocks[instrBlocks[indexMap[target]]];
dstBlock.Sources.Add(srcBlock);
srcBlock.Targets.Add(dstBlock);
}
}
}
for (int i = 0; i < blocks.Count; i++) {
if (blocks[i].Footer.OpCode.FlowControl != FlowControl.Branch &&
blocks[i].Footer.OpCode.FlowControl != FlowControl.Return &&
blocks[i].Footer.OpCode.FlowControl != FlowControl.Throw) {
blocks[i].Targets.Add(blocks[i + 1]);
blocks[i + 1].Sources.Add(blocks[i]);
}
}
}
///
/// Constructs a CFG from the specified method body.
///
/// The method body.
/// The CFG of the given method body.
public static ControlFlowGraph Construct(CilBody body) {
var graph = new ControlFlowGraph(body);
if (body.Instructions.Count == 0)
return graph;
// Populate block headers
var blockHeaders = new HashSet();
var entryHeaders = new HashSet();
graph.PopulateBlockHeaders(blockHeaders, entryHeaders);
// Split blocks
graph.SplitBlocks(blockHeaders, entryHeaders);
// Link blocks
graph.LinkBlocks();
return graph;
}
}
///
/// The type of Control Flow Block
///
[Flags]
public enum ControlFlowBlockType {
///
/// The block is a normal block
///
Normal = 0,
///
/// There are unknown edges to this block. Usually used at exception handlers / method entry.
///
Entry = 1,
///
/// There are unknown edges from this block. Usually used at filter blocks / throw / method exit.
///
Exit = 2
}
///
/// A block in Control Flow Graph (CFG).
///
public class ControlFlowBlock {
///
/// The footer instruction
///
public readonly Instruction Footer;
///
/// The header instruction
///
public readonly Instruction Header;
///
/// The identifier of this block
///
public readonly int Id;
///
/// The type of this block
///
public readonly ControlFlowBlockType Type;
internal ControlFlowBlock(int id, ControlFlowBlockType type, Instruction header, Instruction footer) {
Id = id;
Type = type;
Header = header;
Footer = footer;
Sources = new List();
Targets = new List();
}
///
/// Gets the source blocks of this control flow block.
///
/// The source blocks.
public IList Sources { get; private set; }
///
/// Gets the target blocks of this control flow block.
///
/// The target blocks.
public IList Targets { get; private set; }
///
/// Returns a that represents this block.
///
/// A that represents this block.
public override string ToString() {
return string.Format("Block {0} => {1} {2}", Id, Type, string.Join(", ", Targets.Select(block => block.Id.ToString()).ToArray()));
}
}
}