public void FindExceptionHandlingConstructs() { //The CLR searchese top to bottom through the EH table at the end of the method declaration looking for a CATCH or FILTER handlers //which guarded(try) block encompasses the spot where the exception occurred (FAULT and FINALLY handlers are compeltely ignored at this pass, //no matter whether the exception falls in their guarded(try) blocks). Assuming that CATCH/FILTER handler was found at row N of the EH table // the CLR marks it (but it does NOT execute it at this point) and does a second pass through the EH table searching ONLY rows 1 to N this time. //On hte second pass only FAULT/FINALLY handlers are considered. Any FAULT/FILTER handler found which guarded block encompasses the instruction causing //the exception gets executed at this pass. Only then the CATCH/FILTER handler at row N gets executed. //That means that the order in which handlers appear in the EH table has precedence over their hierarchical structure in the IL (i.e. which try block is inside another try block) when determineing //their order of execution. Hence given a FAULT/FINALLY handler that completely contains a CATCH/FILTER handler in the IL but whose EH table entry occurs before // the CATCH/FILTER handler EH table entry in the LogicalConstruct tree the CATCH/FILTER handler will be parent of the FAULT/FINALLY handler (complete reverse of theri relationship in the IL) //since the FAULT/FINALLY handler will be executed before the CATCH/FILTER one. foreach (ExceptionHandler handler in context.CFG.RawExceptionHandlers) { BlockRange tryBlockRange = GetBlockRangeFromInstructions(handler.TryStart, handler.TryEnd); ExceptionHandlingLogicalConstruct tryBlockExistingHandler = null; if (tryBlocksFound.TryGetValue(tryBlockRange, out tryBlockExistingHandler)) { AddHandlerToTryBlock(tryBlockExistingHandler, handler); } else { BlockLogicalConstruct theTryBlock = CreateExceptionhandlingBlock(tryBlockRange); ExceptionHandlingLogicalConstruct guardedBlock = null; switch (handler.HandlerType) { case ExceptionHandlerType.Catch: guardedBlock = new TryCatchFilterLogicalConstruct(theTryBlock, CreateCatchHandler(handler)); break; case ExceptionHandlerType.Filter: guardedBlock = new TryCatchFilterLogicalConstruct(theTryBlock, CreateFilterHandler(handler)); break; case ExceptionHandlerType.Fault: BlockRange faultBlockRange = GetBlockRangeFromInstructions(handler.HandlerStart, handler.HandlerEnd); guardedBlock = new TryFaultLogicalConstruct(theTryBlock, CreateExceptionhandlingBlock(faultBlockRange)); break; case ExceptionHandlerType.Finally: BlockRange finallyBlockRange = GetBlockRangeFromInstructions(handler.HandlerStart, handler.HandlerEnd); guardedBlock = new TryFinallyLogicalConstruct(theTryBlock, CreateExceptionhandlingBlock(finallyBlockRange)); break; } tryBlocksFound.Add(tryBlockRange, guardedBlock); } } }
internal static CFGBlockLogicalConstruct FindGuardedBlockCFGFollowNode(ExceptionHandlingLogicalConstruct construct, HashSet <CFGBlockLogicalConstruct> outOfconsideration) { //TODO: Don't consider leaves to blocks that end with return. The return can simpy be copied to the place where the leave originates by // the statement decompielr or the goto elimination later. //find all the targets the try/catch/filter/fault/finally blocks in the construct jump/leave to //record the number of leaves to each target Dictionary <CFGBlockLogicalConstruct, uint> numberOfHandlersLeavingToBlock = new Dictionary <CFGBlockLogicalConstruct, uint>(); AddSuccessorsToCount(construct.Try, numberOfHandlersLeavingToBlock); uint leavableConstructsCount = 1; if (construct is TryCatchFilterLogicalConstruct) { TryCatchFilterLogicalConstruct tcf = construct as TryCatchFilterLogicalConstruct; foreach (IFilteringExceptionHandler handler in tcf.Handlers) { AddSuccessorsToCount(handler, numberOfHandlersLeavingToBlock); leavableConstructsCount++; } } else if (construct is TryFaultLogicalConstruct) { AddSuccessorsToCount((construct as TryFaultLogicalConstruct).Fault, numberOfHandlersLeavingToBlock); leavableConstructsCount++; } else if (construct is TryFinallyLogicalConstruct) { AddSuccessorsToCount((construct as TryFinallyLogicalConstruct).Finally, numberOfHandlersLeavingToBlock); leavableConstructsCount++; } //remove the leave targets taht should nto be considered foreach (CFGBlockLogicalConstruct ooc in outOfconsideration) { if (numberOfHandlersLeavingToBlock.ContainsKey(ooc)) { numberOfHandlersLeavingToBlock.Remove(ooc); } } if (numberOfHandlersLeavingToBlock.Count == 0) { return(null); } //find the leave targets that greatest number of exception handling blocks (try/catch/filter/fault/finally) jump to HashSet <CFGBlockLogicalConstruct> mostBlocksExitTo = new HashSet <CFGBlockLogicalConstruct>(); CFGBlockLogicalConstruct randomLeaveTarget = numberOfHandlersLeavingToBlock.Keys.FirstOrDefault <CFGBlockLogicalConstruct>(); mostBlocksExitTo.Add(randomLeaveTarget); uint maxNumberOfLeaveTargetPredecessors = numberOfHandlersLeavingToBlock[randomLeaveTarget]; foreach (CFGBlockLogicalConstruct leaveTarget in numberOfHandlersLeavingToBlock.Keys) { if (numberOfHandlersLeavingToBlock[leaveTarget] > maxNumberOfLeaveTargetPredecessors) { maxNumberOfLeaveTargetPredecessors = numberOfHandlersLeavingToBlock[leaveTarget]; mostBlocksExitTo.Clear(); mostBlocksExitTo.Add(leaveTarget); } else if (numberOfHandlersLeavingToBlock[leaveTarget] == maxNumberOfLeaveTargetPredecessors) { mostBlocksExitTo.Add(leaveTarget); } } //use various heuristics to determine the best follow node //the follow node that will result in the smallest number of gotos is considered superior //the follow node could be changed once loops are created if it turns out loop condition was chosen as a follow node (i.e. we chose a continue edge) if (mostBlocksExitTo.Count == 1) { return(mostBlocksExitTo.FirstOrDefault <CFGBlockLogicalConstruct>()); } else { HashSet <CFGBlockLogicalConstruct> mostLeavesPointTo = new HashSet <CFGBlockLogicalConstruct>(); uint maxLeavesToSingleTarget = 0; foreach (CFGBlockLogicalConstruct leaveTarget in mostBlocksExitTo) { uint leavesToThisTarget = 0; HashSet <CFGBlockLogicalConstruct> constructCFGBlocks = construct.CFGBlocks; foreach (CFGBlockLogicalConstruct leaveTargetpredecessor in leaveTarget.CFGPredecessors) { if (constructCFGBlocks.Contains(leaveTargetpredecessor)) { leavesToThisTarget++; } } if (leavesToThisTarget >= maxLeavesToSingleTarget) { if (leavesToThisTarget > maxLeavesToSingleTarget) { maxLeavesToSingleTarget = leavesToThisTarget; mostLeavesPointTo.Clear(); } mostLeavesPointTo.Add(leaveTarget); } } //TODO: Try anoher heuristics here to chose the follow node that will minimize the number of gotos CFGBlockLogicalConstruct result = mostLeavesPointTo.FirstOrDefault <CFGBlockLogicalConstruct>(); //HEURISTICS: Of all possible follow nodes we try to chose the one which start index is greater than the index of the exception construct start //but less than the start indexes of all other possible candidates. We rely on the assumption that the control flow closely resembles the ordering of // the IL instructions, i.e. if Instr. A comes before Instr. B the chances are that Instr. A will have to be executed before Instr. B in any workflow. // That might not be the case but it's a good assumption since the compilers will try to express teh control flow in the least amount of jumps possible // for performance reasons. if (result.Index < construct.Entry.Index) { CFGBlockLogicalConstruct closestAfterConstruct = null; foreach (CFGBlockLogicalConstruct candidate in mostLeavesPointTo) { if (candidate.Index > construct.Entry.Index) { if (closestAfterConstruct != null && closestAfterConstruct.Index < candidate.Index) { continue; } closestAfterConstruct = candidate; } } if (closestAfterConstruct != null) { result = closestAfterConstruct; } } return(result); } }
public void FindExceptionHandlingConstructs() { V_0 = this.context.get_CFG().get_RawExceptionHandlers().GetEnumerator(); try { while (V_0.MoveNext()) { V_1 = V_0.get_Current(); V_2 = this.GetBlockRangeFromInstructions(V_1.get_TryStart(), V_1.get_TryEnd()); V_3 = null; if (!this.tryBlocksFound.TryGetValue(V_2, out V_3)) { V_4 = this.CreateExceptionhandlingBlock(V_2); V_5 = null; switch (V_1.get_HandlerType()) { case 0: { V_5 = new TryCatchFilterLogicalConstruct(V_4, this.CreateCatchHandler(V_1)); goto Label0; } case 1: { V_5 = new TryCatchFilterLogicalConstruct(V_4, this.CreateFilterHandler(V_1)); goto Label0; } case 2: { V_7 = this.GetBlockRangeFromInstructions(V_1.get_HandlerStart(), V_1.get_HandlerEnd()); V_5 = new TryFinallyLogicalConstruct(V_4, this.CreateExceptionhandlingBlock(V_7)); goto Label0; } case 3: { Label0: this.tryBlocksFound.Add(V_2, V_5); continue; } case 4: { V_6 = this.GetBlockRangeFromInstructions(V_1.get_HandlerStart(), V_1.get_HandlerEnd()); V_5 = new TryFaultLogicalConstruct(V_4, this.CreateExceptionhandlingBlock(V_6)); goto Label0; } default: { goto Label0; } } } else { this.AddHandlerToTryBlock(V_3, V_1); } } } finally { V_0.Dispose(); } return; }