Use non-recursive methods to prevent StackOverflow exceptions

This commit is contained in:
de4dot 2013-11-12 09:41:38 +01:00
parent 6dfe2fd1b6
commit 4fcb332ddf
2 changed files with 76 additions and 33 deletions

View File

@ -170,38 +170,60 @@ namespace de4dot.blocks {
dest.Add(block);
}
struct VisitState {
public BlockInfo Info;
public List<BaseBlock> Targets;
public int TargetIndex;
public BlockInfo TargetInfo;
public VisitState(BlockInfo info) {
this.Info = info;
this.Targets = null;
this.TargetIndex = 0;
this.TargetInfo = null;
}
}
Stack<VisitState> visitStateStack = new Stack<VisitState>();
void Visit(BlockInfo info) {
if (info.baseBlock == firstBlock)
// This method used to be recursive but to prevent stack overflows,
// it's not recursive anymore.
VisitState state = new VisitState(info);
recursive_call:
if (state.Info.baseBlock == firstBlock)
throw new ApplicationException("Can't visit firstBlock");
stack.Push(info);
info.onStack = true;
info.dfsNumber = dfsNumber;
info.low = dfsNumber;
stack.Push(state.Info);
state.Info.onStack = true;
state.Info.dfsNumber = dfsNumber;
state.Info.low = dfsNumber;
dfsNumber++;
foreach (var tmp in GetTargets(info.baseBlock)) {
var targetInfo = GetInfo(tmp);
if (targetInfo == null)
state.Targets = GetTargets(state.Info.baseBlock);
state.TargetIndex = 0;
return_to_caller:
for (; state.TargetIndex < state.Targets.Count; state.TargetIndex++) {
state.TargetInfo = GetInfo(state.Targets[state.TargetIndex]);
if (state.TargetInfo == null)
continue;
if (targetInfo.baseBlock == firstBlock)
if (state.TargetInfo.baseBlock == firstBlock)
continue;
if (!targetInfo.Visited()) {
Visit(targetInfo);
info.low = Math.Min(info.low, targetInfo.low);
if (!state.TargetInfo.Visited()) {
visitStateStack.Push(state);
state = new VisitState(state.TargetInfo);
goto recursive_call;
}
else if (targetInfo.onStack)
info.low = Math.Min(info.low, targetInfo.dfsNumber);
else if (state.TargetInfo.onStack)
state.Info.low = Math.Min(state.Info.low, state.TargetInfo.dfsNumber);
}
if (info.low != info.dfsNumber)
return;
if (state.Info.low != state.Info.dfsNumber)
goto return_from_method;
var sccBlocks = new List<BaseBlock>();
while (true) {
var poppedInfo = stack.Pop();
poppedInfo.onStack = false;
sccBlocks.Add(poppedInfo.baseBlock);
if (ReferenceEquals(info, poppedInfo))
if (ReferenceEquals(state.Info, poppedInfo))
break;
}
if (sccBlocks.Count > 1) {
@ -213,6 +235,14 @@ namespace de4dot.blocks {
else {
sorted.Insert(0, sccBlocks[0]);
}
return_from_method:
if (visitStateStack.Count == 0)
return;
state = visitStateStack.Pop();
state.Info.low = Math.Min(state.Info.low, state.TargetInfo.low);
state.TargetIndex++;
goto return_to_caller;
}
void SortLoopBlock(List<BaseBlock> list) {

View File

@ -100,25 +100,38 @@ namespace de4dot.blocks {
return false;
}
void ScanBaseBlock(BaseBlock bb, int stackStart) {
if (blockInfos.ContainsKey(bb) || !scopeBlock.IsOurBaseBlock(bb))
return;
var blockInfo = new BlockInfo(bb, stackStart);
blockInfos[bb] = blockInfo;
var block = 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;
return;
struct ScanBaseBlockState {
public BaseBlock bb;
public int stackStart;
public ScanBaseBlockState(BaseBlock bb, int stackStart) {
this.bb = bb;
this.stackStart = stackStart;
}
}
Stack<ScanBaseBlockState> scanBaseBlockStack = new Stack<ScanBaseBlockState>();
void ScanBaseBlock(BaseBlock bbx, int stackStartx) {
scanBaseBlockStack.Push(new ScanBaseBlockState(bbx, stackStartx));
while (scanBaseBlockStack.Count > 0) {
var state = scanBaseBlockStack.Pop();
if (blockInfos.ContainsKey(state.bb) || !scopeBlock.IsOurBaseBlock(state.bb))
continue;
blockInfo.CalculateStackUsage();
var blockInfo = new BlockInfo(state.bb, state.stackStart);
blockInfos[state.bb] = blockInfo;
foreach (var target in block.GetTargets())
ScanBaseBlock(target, blockInfo.stackEnd);
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;
}
blockInfo.CalculateStackUsage();
foreach (var target in block.GetTargets())
scanBaseBlockStack.Push(new ScanBaseBlockState(target, blockInfo.stackEnd));
}
}
void CreateNewList() {