de4dot-cex/de4dot.blocks/ForwardScanOrder.cs

181 lines
5.6 KiB
C#
Raw Normal View History

2011-09-22 10:55:30 +08:00
/*
2015-10-30 05:45:26 +08:00
Copyright (C) 2011-2015 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;
namespace de4dot.blocks {
// This class makes sure that each block that is entered with a non-empty stack has at
// least one of its source blocks sorted before itself. This is to make sure peverify
// doesn't complain AND also to make sure dnlib sets the correct maxstack.
2011-09-22 10:55:30 +08:00
class ForwardScanOrder {
ScopeBlock scopeBlock;
IList<BaseBlock> sorted;
Dictionary<BaseBlock, BlockInfo> blockInfos = new Dictionary<BaseBlock, BlockInfo>();
Dictionary<BaseBlock, bool> inNewList = new Dictionary<BaseBlock, bool>();
List<BaseBlock> newList;
class BlockInfo {
BaseBlock baseBlock;
public int stackStart = 0;
public int stackEnd = 0;
public BlockInfo(BaseBlock baseBlock, int stackStart) {
this.baseBlock = baseBlock;
this.stackStart = stackStart;
}
2013-01-19 20:03:57 +08:00
public void CalculateStackUsage() {
2011-09-22 10:55:30 +08:00
Block block = baseBlock as Block;
if (block == null) {
stackEnd = stackStart;
return;
}
int stack = stackStart;
foreach (var instr in block.Instructions)
2012-11-01 00:43:51 +08:00
instr.Instruction.UpdateStack(ref stack, false);
2011-09-22 10:55:30 +08:00
stackEnd = stack;
}
}
public ForwardScanOrder(ScopeBlock scopeBlock, IList<BaseBlock> sorted) {
this.scopeBlock = scopeBlock;
this.sorted = sorted;
}
2013-01-19 20:03:57 +08:00
public List<BaseBlock> Fix() {
CreateBlockInfos();
CreateNewList();
2011-09-22 10:55:30 +08:00
return newList;
}
2013-01-19 20:03:57 +08:00
void CreateBlockInfos() {
2011-09-22 10:55:30 +08:00
int firstBlockStackStart = scopeBlock is TryHandlerBlock ? 1 : 0;
2013-01-19 20:03:57 +08:00
foreach (var bb in GetStartBlocks()) {
2011-09-22 10:55:30 +08:00
int stackStart = ReferenceEquals(bb, sorted[0]) ? firstBlockStackStart : 0;
2013-01-19 20:03:57 +08:00
ScanBaseBlock(bb, stackStart);
2011-09-22 10:55:30 +08:00
}
// One reason for this to fail is if there are still dead blocks left. Could also
// be a bug in the code.
if (blockInfos.Count != sorted.Count)
throw new ApplicationException(string.Format("Didn't add all blocks: {0} vs {1}", blockInfos.Count, sorted.Count));
}
2013-01-19 20:03:57 +08:00
IEnumerable<BaseBlock> GetStartBlocks() {
2011-09-22 10:55:30 +08:00
if (sorted.Count > 0) {
yield return sorted[0];
foreach (var bb in sorted) {
if (ReferenceEquals(bb, sorted[0]))
continue;
var block = bb as Block;
2013-01-19 20:03:57 +08:00
if (block == null || block.Sources == null || IsOneSourceInAnotherScopeBlock(block))
2011-09-22 10:55:30 +08:00
yield return bb;
}
}
}
2013-01-19 20:03:57 +08:00
bool IsOneSourceInAnotherScopeBlock(Block block) {
2011-09-22 10:55:30 +08:00
foreach (var source in block.Sources) {
2013-01-19 20:03:57 +08:00
if (!scopeBlock.IsOurBaseBlock(source))
2011-09-22 10:55:30 +08:00
return true;
}
return false;
}
struct ScanBaseBlockState {
public BaseBlock bb;
public int stackStart;
public ScanBaseBlockState(BaseBlock bb, int stackStart) {
this.bb = bb;
this.stackStart = stackStart;
2011-09-22 10:55:30 +08:00
}
}
Stack<ScanBaseBlockState> scanBaseBlockStack = new Stack<ScanBaseBlockState>();
2013-11-13 05:02:19 +08:00
void ScanBaseBlock(BaseBlock bb, int stackStart) {
scanBaseBlockStack.Push(new ScanBaseBlockState(bb, stackStart));
while (scanBaseBlockStack.Count > 0) {
var state = scanBaseBlockStack.Pop();
if (blockInfos.ContainsKey(state.bb) || !scopeBlock.IsOurBaseBlock(state.bb))
continue;
var blockInfo = new BlockInfo(state.bb, state.stackStart);
blockInfos[state.bb] = blockInfo;
var block = state.bb as Block;
if (block == null) { // i.e., if try, filter, or handler block
// It's not important to know the exact values, so we set them both to 0.
// Compilers must make sure the stack is empty when entering a try block.
blockInfo.stackStart = blockInfo.stackEnd = 0;
continue;
}
2011-09-22 10:55:30 +08:00
blockInfo.CalculateStackUsage();
2011-09-22 10:55:30 +08:00
foreach (var target in block.GetTargets())
scanBaseBlockStack.Push(new ScanBaseBlockState(target, blockInfo.stackEnd));
}
2011-09-22 10:55:30 +08:00
}
2013-01-19 20:03:57 +08:00
void CreateNewList() {
2011-09-22 10:55:30 +08:00
newList = new List<BaseBlock>(sorted.Count);
foreach (var bb in sorted)
2013-01-19 20:03:57 +08:00
AddToNewList(bb);
2011-09-22 10:55:30 +08:00
if (newList.Count != sorted.Count)
throw new ApplicationException(string.Format("Too many/few blocks after sorting: {0} vs {1}", newList.Count, sorted.Count));
if (newList.Count > 0 && !ReferenceEquals(newList[0], sorted[0]))
throw new ApplicationException("Start block is not first block after sorting");
}
2013-01-19 20:03:57 +08:00
void AddToNewList(BaseBlock bb) {
if (inNewList.ContainsKey(bb) || !scopeBlock.IsOurBaseBlock(bb))
2011-09-22 10:55:30 +08:00
return;
2011-12-26 08:03:39 +08:00
inNewList[bb] = false;
2011-09-22 10:55:30 +08:00
var blockInfo = blockInfos[bb];
var block = bb as Block;
if (blockInfo.stackStart == 0 || ReferenceEquals(bb, sorted[0]) ||
2013-01-19 20:03:57 +08:00
block == null || block.Sources == null || IsInNewList(block.Sources)) {
2011-09-22 10:55:30 +08:00
}
else {
foreach (var source in block.Sources) {
2013-01-19 20:03:57 +08:00
if (!scopeBlock.IsOurBaseBlock(source))
2011-12-26 08:03:39 +08:00
continue;
int oldCount = newList.Count;
2013-01-19 20:03:57 +08:00
AddToNewList(source); // Make sure it's before this block
2011-12-26 08:03:39 +08:00
if (oldCount != newList.Count)
2011-09-22 10:55:30 +08:00
break;
}
}
2011-12-26 08:03:39 +08:00
inNewList[bb] = true;
2011-09-22 10:55:30 +08:00
newList.Add(bb);
}
2013-01-19 20:03:57 +08:00
bool IsInNewList(IEnumerable<Block> blocks) {
2011-09-22 10:55:30 +08:00
foreach (var block in blocks) {
2011-12-26 08:03:39 +08:00
if (inNewList.ContainsKey(block) && inNewList[block])
2011-09-22 10:55:30 +08:00
return true;
}
return false;
}
}
}