/** * Create the edges in the control flow graph with the basic blocks. * * @param insts the instructions * @param numBlocks an array containing the number of basic block for each instruction. * @param bbs the basic blocks */ private void findCFGEdges(Instruction[] insts, short[] numBlocks, BasicBlock[] bbs) { for (int i = 0; i < bbs.length; ++i) { BasicBlock bb = bbs[i]; Instruction lastInst = insts[bb.getEnd()]; if (lastInst.canComplete() && i != bbs.length - 1) { // the next block is accessible bb.addDefaultNextBlock(bbs[i + 1]); } if (lastInst instanceof JumpInstruction) { // goto, jsr are distincts than other jump instructions. Edge edge; JumpInstruction jumpInst = (JumpInstruction) lastInst; if (lastInst.getOpcode() == ClassfileConstants2.opc_goto) { edge = bb.addDefaultNextBlock( bbs[numBlocks[getInstructionLine(insts, jumpInst.getTarget())]]); jumpInst.setTarget(new EdgeLabel(edge)); } else if (lastInst.getOpcode() == ClassfileConstants2.opc_jsr) { // do nothing here // see method findSubRoutines } else { edge = bb.addConditionNextBlock( bbs[numBlocks[getInstructionLine(insts, jumpInst.getTarget())]]); jumpInst.setTarget(new EdgeLabel(edge)); } } if (lastInst instanceof SwitchInstruction) { SwitchInstruction si = (SwitchInstruction) lastInst; for (int k = -1; k < si.getSwitchCount(); k++) { Edge edge = bb.addSwitchNextBlock(bbs[numBlocks[getInstructionLine(insts, si.getTarget(k))]]); si.setTarget(k, new EdgeLabel(edge)); } } } }
/** * Return an array corresponding to the array <code>insts</code> and mark if an instruction is the * start point of a basic block. * * @param insts the array of instructions * @return an array which the same length and the values are tab[i] = 1 if insts[i] is a start * instruction of a basic block, 0 else. */ private short[] markStartblock(Instruction[] insts) { int length = insts.length; short[] startblock = new short[length]; // initialized to zero. for (int j = 0; j < length; j++) { // if the curent instruction is a jump, the destination // instruction and the next instruction are marked as a // begining of a basic block if (insts[j] instanceof JumpInstruction) { if (j + 1 < length) { startblock[j + 1] = 1; } JumpInstruction ji = (JumpInstruction) insts[j]; startblock[getInstructionLine(insts, ji.getTarget())] = 1; } // all labels of a switch start a basic block. if (insts[j] instanceof SwitchInstruction) { SwitchInstruction si = (SwitchInstruction) insts[j]; for (int k = -1; k < si.getSwitchCount(); k++) { startblock[getInstructionLine(insts, si.getTarget(k))] = 1; } } // if the next instruction is not reached, this a new basic block. if (!insts[j].canComplete() && j + 1 < length) { startblock[j + 1] = 1; } } // mark the beginning of try block as a new basic block. // the instruction after the last instruction of a try block // is also a start point of a basic block. // The catch block is also a new basic block. for (int j = 0; j < exceptionHandlers.length; ++j) { startblock[exceptionHandlers[j].getStart()] = 1; int afterEnd = exceptionHandlers[j].getEnd() + 1; if (afterEnd < length) { startblock[afterEnd] = 1; } startblock[exceptionHandlers[j].getHandle()] = 1; } return startblock; }
/** * Find subroutines in the method. Create an object SubRoutine for each one. Add the edges. * * @param numBlocks an array containing the number of basic block for each instruction. * @param insts the instructions * @param bbs the basic blocks */ public void findSubRoutines(short[] numBlocks, final Instruction[] insts, BasicBlock[] bbs) { for (int i = 0; i < bbs.length; ++i) { Instruction lastInst = insts[bbs[i].getEnd()]; if (lastInst.getOpcode() == ClassfileConstants2.opc_jsr) { JumpInstruction jumpInst = (JumpInstruction) lastInst; BasicBlock subroutineCall = bbs[i]; BasicBlock subroutineReturn = bbs[i + 1]; final BasicBlock startSubroutine = bbs[numBlocks[getInstructionLine(insts, jumpInst.getTarget())]]; SubRoutine sub = (SubRoutine) subroutines.get(startSubroutine); if (sub == null) { startSubroutine.setFirstBlockSubroutine(true); // we have to create the SubRoutine object // so we research the end block of the subroutine // we follow a path from start block until we find the ret final Map subs = subroutines; final BasicBlock currentBB = startSubroutine; // the search will not enter into nested subroutine // because there is an edge after a block with // a jsr and its next block. graph.visitGraphFromNode( currentBB, new NodeVisitor() { public boolean visit(Node n) { BasicBlock bb = (BasicBlock) n; Instruction last = insts[bb.getEnd()]; if (last.getOpcode() == ClassfileConstants2.opc_ret) { // we found the end of the subroutine SubRoutine s = new SubRoutine(startSubroutine, currentBB); subs.put(startSubroutine, s); return false; } return true; } }); if ((sub = (SubRoutine) subroutines.get(startSubroutine)) == null) { // error throw new RuntimeException("NO RETURN TO SUBROUTINE"); } } // add edges Edge edgeCall = subroutineCall.addSubRoutineCallNextBlock(startSubroutine); jumpInst.setTarget(new EdgeLabel(edgeCall)); Edge edgeReturn = sub.getEnd().addSubRoutineReturnNextBlock(subroutineReturn); sub.addCall(edgeCall, edgeReturn); } } // remove all edges from basic blocks which end by jsr with // its next block. for (int i = 0; i < bbs.length; ++i) { Instruction lastInst = insts[bbs[i].getEnd()]; if (lastInst.getOpcode() == ClassfileConstants2.opc_jsr) { BasicBlock subroutineCall = bbs[i]; BasicBlock subroutineReturn = bbs[i + 1]; subroutineCall.removeSuccessor(subroutineReturn); } } }