de4dot-cex/de4dot.blocks/Block.cs

321 lines
9.1 KiB
C#
Raw Normal View History

2011-09-22 10:55:30 +08:00
/*
2014-03-12 05:15:43 +08:00
Copyright (C) 2011-2014 de4dot@gmail.com
2011-09-22 10:55:30 +08:00
This file is part of de4dot.
de4dot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
de4dot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using dnlib.DotNet.Emit;
2011-09-22 10:55:30 +08:00
namespace de4dot.blocks {
2011-09-24 16:26:29 +08:00
public class Block : BaseBlock {
2011-09-22 10:55:30 +08:00
List<Instr> instructions = new List<Instr>();
// List of all explicit (non-fall-through) targets. It's just one if it's a normal
// branch, but if it's a switch, it could be many targets.
List<Block> targets;
// This is the fall through Block (non branch instructions)
Block fallThrough;
// All blocks that fall through or branches to this block
List<Block> sources = new List<Block>();
public Block FallThrough {
get { return fallThrough; }
set { fallThrough = value; }
}
public List<Block> Targets {
get { return targets; }
set { targets = value; }
}
public List<Block> Sources {
get { return sources; }
}
public Instr FirstInstr {
get {
if (instructions.Count == 0)
2013-01-19 20:03:57 +08:00
Add(new Instr(OpCodes.Nop.ToInstruction()));
2011-09-22 10:55:30 +08:00
return instructions[0];
}
}
public Instr LastInstr {
get {
if (instructions.Count == 0)
2013-01-19 20:03:57 +08:00
Add(new Instr(OpCodes.Nop.ToInstruction()));
2011-09-22 10:55:30 +08:00
return instructions[instructions.Count - 1];
}
}
2013-01-19 20:03:57 +08:00
public void Add(Instr instr) {
2011-09-22 10:55:30 +08:00
instructions.Add(instr);
}
2013-01-19 20:03:57 +08:00
public void Insert(int index, Instruction instr) {
2011-09-22 10:55:30 +08:00
instructions.Insert(index, new Instr(instr));
}
public List<Instr> Instructions {
get { return instructions; }
}
// If last instr is a br/br.s, removes it and replaces it with a fall through
2013-01-19 20:03:57 +08:00
public void RemoveLastBr() {
if (!LastInstr.IsBr())
2011-09-22 10:55:30 +08:00
return;
if (fallThrough != null || (LastInstr.Operand != null && (targets == null || targets.Count != 1)))
2011-09-22 10:55:30 +08:00
throw new ApplicationException("Invalid block state when last instr is a br/br.s");
fallThrough = LastInstr.Operand != null ? targets[0] : null;
2011-09-22 10:55:30 +08:00
targets = null;
instructions.RemoveAt(instructions.Count - 1);
}
2013-01-19 20:03:57 +08:00
public void Replace(int index, int num, Instruction instruction) {
2011-09-22 10:55:30 +08:00
if (num <= 0)
throw new ArgumentOutOfRangeException("num");
2013-01-19 20:03:57 +08:00
Remove(index, num);
2011-09-22 10:55:30 +08:00
instructions.Insert(index, new Instr(instruction));
}
2013-01-19 20:03:57 +08:00
public void Remove(int index, int num) {
2011-09-22 10:55:30 +08:00
if (index + num > instructions.Count)
throw new ApplicationException("Overflow");
2013-01-19 20:03:57 +08:00
if (num > 0 && index + num == instructions.Count && LastInstr.IsConditionalBranch())
DisconnectFromFallThroughAndTargets();
2011-09-22 10:55:30 +08:00
instructions.RemoveRange(index, num);
}
2013-01-19 20:03:57 +08:00
public void Remove(IEnumerable<int> indexes) {
var instrsToDelete = new List<int>(Utils.Unique(indexes));
2011-09-22 10:55:30 +08:00
instrsToDelete.Sort();
instrsToDelete.Reverse();
foreach (var index in instrsToDelete)
2013-01-19 20:03:57 +08:00
Remove(index, 1);
2011-09-22 10:55:30 +08:00
}
// Replace the last instructions with a branch to target
2013-01-19 20:03:57 +08:00
public void ReplaceLastInstrsWithBranch(int numInstrs, Block target) {
2011-09-22 10:55:30 +08:00
if (numInstrs < 0 || numInstrs > instructions.Count)
throw new ApplicationException("Invalid numInstrs to replace with branch");
if (target == null)
throw new ApplicationException("Invalid new target, it's null");
2013-01-19 20:03:57 +08:00
DisconnectFromFallThroughAndTargets();
2011-09-22 10:55:30 +08:00
if (numInstrs > 0)
instructions.RemoveRange(instructions.Count - numInstrs, numInstrs);
fallThrough = target;
target.sources.Add(this);
}
2013-01-19 20:03:57 +08:00
public void ReplaceLastNonBranchWithBranch(int numInstrs, Block target) {
if (LastInstr.IsBr())
2011-09-22 10:55:30 +08:00
numInstrs++;
2013-01-19 20:03:57 +08:00
ReplaceLastInstrsWithBranch(numInstrs, target);
2011-09-22 10:55:30 +08:00
}
2013-01-19 20:03:57 +08:00
public void ReplaceBccWithBranch(bool isTaken) {
2011-10-17 06:22:22 +08:00
Block target = isTaken ? targets[0] : fallThrough;
2013-01-19 20:03:57 +08:00
ReplaceLastInstrsWithBranch(1, target);
2011-10-17 06:22:22 +08:00
}
2013-01-19 20:03:57 +08:00
public void ReplaceSwitchWithBranch(Block target) {
2011-10-18 14:17:21 +08:00
if (LastInstr.OpCode.Code != Code.Switch)
throw new ApplicationException("Last instruction is not a switch");
2013-01-19 20:03:57 +08:00
ReplaceLastInstrsWithBranch(1, target);
2011-10-18 14:17:21 +08:00
}
2013-01-19 20:03:57 +08:00
public void SetNewFallThrough(Block newFallThrough) {
DisconnectFromFallThrough();
2011-12-15 17:04:04 +08:00
fallThrough = newFallThrough;
newFallThrough.sources.Add(this);
}
2013-01-19 20:03:57 +08:00
public void SetNewTarget(int index, Block newTarget) {
DisconnectFromBlock(targets[index]);
2011-12-15 17:04:04 +08:00
targets[index] = newTarget;
newTarget.sources.Add(this);
}
2013-01-19 20:03:57 +08:00
public void RemoveDeadBlock() {
2011-09-22 10:55:30 +08:00
if (sources.Count != 0)
throw new ApplicationException("Trying to remove a non-dead block");
2013-01-19 20:03:57 +08:00
RemoveGuaranteedDeadBlock();
2011-09-22 10:55:30 +08:00
}
// Removes a block that has been guaranteed to be dead. This method won't verify
// that it really is dead.
2013-01-19 20:03:57 +08:00
public void RemoveGuaranteedDeadBlock() {
DisconnectFromFallThroughAndTargets();
2011-09-22 10:55:30 +08:00
Parent = null;
}
2013-01-19 20:03:57 +08:00
void DisconnectFromFallThroughAndTargets() {
DisconnectFromFallThrough();
DisconnectFromTargets();
2011-09-22 10:55:30 +08:00
}
2013-01-19 20:03:57 +08:00
void DisconnectFromFallThrough() {
2011-09-22 10:55:30 +08:00
if (fallThrough != null) {
2013-01-19 20:03:57 +08:00
DisconnectFromBlock(fallThrough);
2011-09-22 10:55:30 +08:00
fallThrough = null;
}
}
2013-01-19 20:03:57 +08:00
void DisconnectFromTargets() {
2011-09-22 10:55:30 +08:00
if (targets != null) {
foreach (var target in targets)
2013-01-19 20:03:57 +08:00
DisconnectFromBlock(target);
2011-09-22 10:55:30 +08:00
targets = null;
}
}
2013-01-19 20:03:57 +08:00
void DisconnectFromBlock(Block target) {
2011-09-22 10:55:30 +08:00
if (!target.sources.Remove(this))
throw new ApplicationException("Could not remove the block from its target block");
}
2013-01-19 20:03:57 +08:00
public int CountTargets() {
2011-09-22 10:55:30 +08:00
int count = fallThrough != null ? 1 : 0;
if (targets != null)
count += targets.Count;
return count;
}
// Returns the target iff it has only ONE target. Else it returns null.
2013-01-19 20:03:57 +08:00
public Block GetOnlyTarget() {
if (CountTargets() != 1)
2011-09-22 10:55:30 +08:00
return null;
if (fallThrough != null)
return fallThrough;
return targets[0];
}
// Returns all targets. FallThrough (if not null) is always returned first!
2013-01-19 20:03:57 +08:00
public IEnumerable<Block> GetTargets() {
2011-09-22 10:55:30 +08:00
if (fallThrough != null)
yield return fallThrough;
if (targets != null) {
foreach (var block in targets)
yield return block;
}
}
// Returns true iff other is the only block in Sources
2013-01-19 20:03:57 +08:00
public bool IsOnlySource(Block other) {
2011-09-22 10:55:30 +08:00
return sources.Count == 1 && sources[0] == other;
}
// Returns true if we can merge other with this
2013-01-19 20:03:57 +08:00
public bool CanMerge(Block other) {
return CanAppend(other) && other.IsOnlySource(this);
2011-09-22 10:55:30 +08:00
}
// Merge two blocks into one
2013-01-19 20:03:57 +08:00
public void Merge(Block other) {
if (!CanMerge(other))
2011-09-22 10:55:30 +08:00
throw new ApplicationException("Can't merge the two blocks!");
2013-01-19 20:03:57 +08:00
Append(other);
other.DisconnectFromFallThroughAndTargets();
2012-01-02 21:22:51 +08:00
other.Parent = null;
}
2013-01-19 20:03:57 +08:00
public bool CanAppend(Block other) {
if (other == null || other == this || GetOnlyTarget() != other)
2012-01-02 21:22:51 +08:00
return false;
// If it's eg. a leave, then don't merge them since it clears the stack.
2013-01-19 20:03:57 +08:00
return LastInstr.IsBr() || Instr.IsFallThrough(LastInstr.OpCode);
2012-01-02 21:22:51 +08:00
}
2013-01-19 20:03:57 +08:00
public void Append(Block other) {
if (!CanAppend(other))
2012-01-02 21:22:51 +08:00
throw new ApplicationException("Can't append the block!");
2011-09-22 10:55:30 +08:00
2013-01-19 20:03:57 +08:00
RemoveLastBr(); // Get rid of last br/br.s if present
2011-09-22 10:55:30 +08:00
2012-01-02 21:22:51 +08:00
var newInstructions = new List<Instr>(instructions.Count + other.instructions.Count);
2013-01-19 20:03:57 +08:00
AddInstructions(newInstructions, instructions, false);
AddInstructions(newInstructions, other.instructions, true);
2011-09-22 10:55:30 +08:00
instructions = newInstructions;
2013-01-19 20:03:57 +08:00
DisconnectFromFallThroughAndTargets();
2011-09-22 10:55:30 +08:00
if (other.targets != null)
targets = new List<Block>(other.targets);
else
targets = null;
fallThrough = other.fallThrough;
2013-01-19 20:03:57 +08:00
UpdateSources();
2011-09-22 10:55:30 +08:00
}
2013-01-19 20:03:57 +08:00
void AddInstructions(IList<Instr> dest, IList<Instr> instrs, bool clone) {
2012-01-27 07:30:53 +08:00
for (int i = 0; i < instrs.Count; i++) {
var instr = instrs[i];
if (instr.OpCode != OpCodes.Nop)
2012-11-01 00:43:51 +08:00
dest.Add(clone ? new Instr(instr.Instruction.Clone()) : instr);
2011-09-22 10:55:30 +08:00
}
}
// Update each target's Sources property. Must only be called if this isn't in the
// Sources list!
2013-01-19 20:03:57 +08:00
public void UpdateSources() {
2011-09-22 10:55:30 +08:00
if (fallThrough != null)
fallThrough.sources.Add(this);
if (targets != null) {
foreach (var target in targets)
target.sources.Add(this);
}
}
// Returns true if it falls through
2013-01-19 20:03:57 +08:00
public bool IsFallThrough() {
2011-09-22 10:55:30 +08:00
return targets == null && fallThrough != null;
}
2013-01-19 20:03:57 +08:00
public bool CanFlipConditionalBranch() {
return LastInstr.CanFlipConditionalBranch();
2011-09-22 10:55:30 +08:00
}
2013-01-19 20:03:57 +08:00
public void FlipConditionalBranch() {
2011-09-22 10:55:30 +08:00
if (fallThrough == null || targets == null || targets.Count != 1)
throw new ApplicationException("Invalid bcc block state");
2013-01-19 20:03:57 +08:00
LastInstr.FlipConditonalBranch();
2011-09-22 10:55:30 +08:00
var oldFallThrough = fallThrough;
fallThrough = targets[0];
targets[0] = oldFallThrough;
}
// Returns true if it's a conditional branch
2013-01-19 20:03:57 +08:00
public bool IsConditionalBranch() {
return LastInstr.IsConditionalBranch();
2011-09-22 10:55:30 +08:00
}
2011-12-15 17:04:04 +08:00
2013-01-19 20:03:57 +08:00
public bool IsNopBlock() {
if (!IsFallThrough())
2011-12-15 17:04:04 +08:00
return false;
foreach (var instr in instructions) {
if (instr.OpCode.Code != Code.Nop)
return false;
}
return true;
}
2011-09-22 10:55:30 +08:00
}
}