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); 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) { 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"); throw new ApplicationException("Can't visit firstBlock");
stack.Push(info); stack.Push(state.Info);
info.onStack = true; state.Info.onStack = true;
info.dfsNumber = dfsNumber; state.Info.dfsNumber = dfsNumber;
info.low = dfsNumber; state.Info.low = dfsNumber;
dfsNumber++; dfsNumber++;
foreach (var tmp in GetTargets(info.baseBlock)) { state.Targets = GetTargets(state.Info.baseBlock);
var targetInfo = GetInfo(tmp); state.TargetIndex = 0;
if (targetInfo == null) return_to_caller:
for (; state.TargetIndex < state.Targets.Count; state.TargetIndex++) {
state.TargetInfo = GetInfo(state.Targets[state.TargetIndex]);
if (state.TargetInfo == null)
continue; continue;
if (targetInfo.baseBlock == firstBlock) if (state.TargetInfo.baseBlock == firstBlock)
continue; continue;
if (!targetInfo.Visited()) { if (!state.TargetInfo.Visited()) {
Visit(targetInfo); visitStateStack.Push(state);
info.low = Math.Min(info.low, targetInfo.low); state = new VisitState(state.TargetInfo);
goto recursive_call;
} }
else if (targetInfo.onStack) else if (state.TargetInfo.onStack)
info.low = Math.Min(info.low, targetInfo.dfsNumber); state.Info.low = Math.Min(state.Info.low, state.TargetInfo.dfsNumber);
} }
if (info.low != info.dfsNumber) if (state.Info.low != state.Info.dfsNumber)
return; goto return_from_method;
var sccBlocks = new List<BaseBlock>(); var sccBlocks = new List<BaseBlock>();
while (true) { while (true) {
var poppedInfo = stack.Pop(); var poppedInfo = stack.Pop();
poppedInfo.onStack = false; poppedInfo.onStack = false;
sccBlocks.Add(poppedInfo.baseBlock); sccBlocks.Add(poppedInfo.baseBlock);
if (ReferenceEquals(info, poppedInfo)) if (ReferenceEquals(state.Info, poppedInfo))
break; break;
} }
if (sccBlocks.Count > 1) { if (sccBlocks.Count > 1) {
@ -213,6 +235,14 @@ namespace de4dot.blocks {
else { else {
sorted.Insert(0, sccBlocks[0]); 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) { void SortLoopBlock(List<BaseBlock> list) {

View File

@ -100,25 +100,38 @@ namespace de4dot.blocks {
return false; return false;
} }
void ScanBaseBlock(BaseBlock bb, int stackStart) { struct ScanBaseBlockState {
if (blockInfos.ContainsKey(bb) || !scopeBlock.IsOurBaseBlock(bb)) public BaseBlock bb;
return; public int stackStart;
public ScanBaseBlockState(BaseBlock bb, int stackStart) {
var blockInfo = new BlockInfo(bb, stackStart); this.bb = bb;
blockInfos[bb] = blockInfo; this.stackStart = stackStart;
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;
} }
}
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()) var block = state.bb as Block;
ScanBaseBlock(target, blockInfo.stackEnd); 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() { void CreateNewList() {