public FinallyBlockDuplicator ( ControlFlowGraph cfg, Method method, IList/*<Block>*/ new_blocks, Hashtable/*<Block,Block>*/ block2next, NormalFlowBlockNavigator nfbnav, IList/*<ExceptionHandler>*/ all_ehs ) { this.dupVisitor = new DupVisitor(method.DeclaringType.DeclaringModule, method.DeclaringType); this.cfg = cfg; this.method = method; this.new_blocks = new_blocks; this.block2next = block2next; this.bnav = new UnionGraphNavigator(nfbnav, new ControlFlowGraph.ExcpFlowBlockNavigator(cfg)); this.allExceptionHandlers = all_ehs; // init the block -> index map this.b2index = new Hashtable(); StatementList blocks = method.Body.Statements; for(int i = 0; i < blocks.Count; i++) { b2index[(Block) blocks[i]] = i; } // init the exception handler -> last block map this.lastHandledBlock = new Hashtable(); foreach (ExceptionHandler eh in this.allExceptionHandlers) { if (eh.HandlerType != NodeType.Finally && eh.HandlerType != NodeType.FaultHandler) { continue; } this.lastHandledBlock[eh] = LastBlockInsideHandler(eh); } // 2. deal with the "leave" instructions TreatLeaveInstructions (); // 3. The original finally / fault handlers should be turned into catch handlers ConvertFinallyHandlerIntoCatchHandler(); }
private HashSet/*<Block>*/ filter_blocks (Method method, NormalFlowBlockNavigator nfbnav) { HashSet/*<Block>*/ blocksToKill = new HashSet(); /* We no longer remove the filter blocks. ExceptionHandlerList ehs = method.ExceptionHandlers; for (int i=0; i<ehs.Length; i++) if (ehs[i].HandlerType == NodeType.Filter) add_block_plus_succ_to_set(blocksToKill, ehs[i].FilterExpression, nfbnav); */ return blocksToKill; }
/// <summary> /// Constructs the Control Flow Graph for <c>method</c>. /// If the code for <c>method</c> is unavailable, throw an <c>UnavailableCodeException</c> /// (a constructor cannot return an error code). If the code for <c>method</c> is empty /// (abstract method), thrown a <c>NoCodeException</c>. Otherwise, examine the CciHelper /// representation of the method code and construct the CFG. /// </summary> /// <param name="method">Method whose CFG will be constructed.</param> /// <param name="duplicateFinallyBlocks">If <c>true</c>, the finally blocks will be duplicated and you'll obtain a real /// CFG. Otherwise, you'll have to manually deal with the finally blocks that are traversed by each /// "leave" instruction. HIGHLY RECOMMENDED!</param> /// <param name="eliminateEvaluationStack">If <c>true</c>, <c>StackRemovalTransf.Process</c> will be called /// to remove the stack manipulating operations. See more commends in the class /// <c>StackRemovalTransf</c>. RECOMMENDED!</param> /// <param name="constantFoldBranches">When <c>true</c>, use constant folding /// to prune infeasible branches.</param> public ControlFlowGraph (Method method, bool duplicateFinallyBlocks, bool eliminateEvaluationStack, bool expandAllocations, bool constantFoldBranches) { StatementList blocks = method.Body.Statements; if (blocks == null) throw new UnavailableCodeException(method); if (blocks.Count == 0) throw new NoCodeException(method); this.originalMethod = method; // Hold onto the original because MakeFlat modifies the method // COMMENT NEXT LINE IF YOU HAVE PROBLEMS! method = CodeFlattener.MakeFlat(method, expandAllocations, constantFoldBranches, out codeMap); this.method = method; // useful for debugging // reload blocks, since MakeFlat changed this. blocks = method.Body.Statements; //Console.WriteLine("Before removing last block...\n"); //CodePrinter.PrintMethod(Console.Out, method); /* Block lastblock = (Block)blocks[blocks.Length-1]; if (lastblock.Statements.Length==0) { // dummy block, remove it. StatementList oldblocks = blocks; blocks = new StatementList(oldblocks.Length-1); for (int i=0;i<oldblocks.Length-1; i++) { blocks.Add(oldblocks[i]); } method.Body.Statements = blocks; } */ // Debug code with code printer. //Console.WriteLine("Before code flattening...\n"); //CodePrinter.PrintMethod(Console.Out, method); CreateEntryAndExitBlocks(method, blocks); // 0. build map block2next that gives the fall-through successor of each block b2next = new Hashtable(); for(int i = 0; i < blocks.Count-1; i++) { b2next[blocks[i]] = blocks[i+1]; } // add fall through from special entry to real entry block b2next[this.Entry] = blocks[0]; NormalFlowBlockNavigator nfbnav = new NormalFlowBlockNavigator(this, this.b2next); HashSet/*<Block>*/ filterblocks = filter_blocks(method,nfbnav); IList/*<ExceptionHandler>*/ all_ehs; // 1. for each block, find the array of relevant handlers // (order consistent with the order of the Exception Handlers) bool has_finally = BuildExceptionalFlow(method, blocks, out all_ehs, filterblocks); // SIDE EFFECT: build b2e_succ map IList new_blocks = new ArrayList(); if (has_finally && duplicateFinallyBlocks) { // 2. eliminate finallies if (FINALLY_CLONE_DEBUG) { this.b2cloning_leave = new Hashtable(); } FinallyBlockDuplicator fdc = new FinallyBlockDuplicator(this, method, new_blocks, this.b2next, nfbnav, all_ehs); if (FINALLY_CLONE_DEBUG) { this.copy2orig = fdc.copy2orig; } } // 3. add catch statements to all handlers AddCatchStatements(all_ehs, new_blocks); // 4. build the collection all_blocks; BuildAllBlockCollection(method, new_blocks, filterblocks, nfbnav); // 4a. fixup back jumps in handlers going to the handler head. They must go to the // immediately succeeding block to skip the new "catch" instruction. FixupJumpsToHandlerHead(); // 5. build the map block b -> blocks protected by the handler that starts at b; BuildHandlerPredecessorMap(); // 6. build the normal flow BuildNormalFlow(all_blocks, nfbnav, (CfgBlock) blocks[0]); // 7. build continuation map BuildContinuationMap(all_blocks); #if DEBUGxxx Console.WriteLine(); Console.WriteLine("================================================================="); Console.WriteLine(); this.Display(Console.Out); #endif if (eliminateEvaluationStack) { // sets the stack depth of each block as a side effect. StackRemovalTransformation.Process(this); } #if DEBUG_EXPENSIVE this.TestInvariants(); this.Seal(); #endif }
private void BuildNormalFlow ( CfgBlock[] blocks, NormalFlowBlockNavigator nfbnav, CfgBlock real_entry ) { IMutableRelation/*<Block,Block>*/ n_succ = new BlockRelation(); foreach(Block block in blocks) { if (block is ISpecialBlock) continue; n_succ.AddAll(block, nfbnav.NextNodes(block)); } // common sense rules n_succ.Add(entry_point, real_entry); n_succ.Add(normal_exit_point, exit_point); n_succ.Add(excp_exit_point, exit_point); IRelation/*<Block,Block>*/ n_pred = n_succ.Reverse(); b2n_succ = Relation.Compact(n_succ, typeof(CfgBlock)); b2n_pred = Relation.Compact(n_pred, typeof(CfgBlock)); }
// build the array all_blocks: the original blocks + the block clones (if any) + special blocks private void BuildAllBlockCollection ( Method method, IList/*<CfgBlock>*/ new_blocks, ISet/*<CfgBlock>*/ filterblocks, NormalFlowBlockNavigator nfnav ) { IMutableSet reach = new HashSet(); FindReachable(reach, (CfgBlock)method.Body.Statements[0], nfnav); int nb_blocks = reach.Count + 2; // entry and exit if (!reach.Contains(this.normal_exit_point)) { nb_blocks++; } if (!reach.Contains(this.excp_exit_point)) { nb_blocks++; } CfgBlock[] all_blocks; this.all_blocks = all_blocks = new CfgBlock[nb_blocks]; int index = 0; AddBlock(this.entry_point, all_blocks, ref index); foreach (CfgBlock block in reach) { AddBlock(block, all_blocks, ref index); } #if false int nb_blocks = method.Body.Statements.Count - filterblocks.Count + 4; nb_blocks += new_blocks.Count; CfgBlock[] all_blocks; this.all_blocks = all_blocks = new CfgBlock[nb_blocks]; StatementList blocks = method.Body.Statements; int index = 0; AddBlock(this.entry_point, all_blocks, ref index); for(int i = 0 ; i < blocks.Count; i++) { if ( ! filterblocks.Contains(blocks[i])) { AddBlock((CfgBlock)blocks[i], all_blocks, ref index); } } if (new_blocks != null) { // adding the block clones to all_blocks foreach(CfgBlock block in new_blocks) { AddBlock(block, all_blocks, ref index); } } #endif // adding the three special blocks if (!reach.Contains(this.normal_exit_point)) { AddBlock(this.normal_exit_point, all_blocks, ref index); } if (!reach.Contains(this.excp_exit_point)) { AddBlock(this.excp_exit_point, all_blocks, ref index); } AddBlock(this.exit_point, all_blocks, ref index); }
private void FindReachable(IMutableSet reach, CfgBlock current, NormalFlowBlockNavigator nfnav) { if (reach.Contains(current)) return; reach.Add(current); foreach (CfgBlock next in nfnav.NextNodes(current)) { FindReachable(reach, next, nfnav); } // also follow exceptional path CfgBlock handler = (CfgBlock)this.b2exception_handler[current]; if (handler != null) { FindReachable(reach, handler, nfnav); } }