public static boolean isReturnBlocks(BlockNode b1, BlockNode b2) { if (!b1.isReturnBlock() || !b2.isReturnBlock()) { return false; } List<InsnNode> b1Insns = b1.getInstructions(); List<InsnNode> b2Insns = b2.getInstructions(); if (b1Insns.size() != 1 || b2Insns.size() != 1) { return false; } InsnNode i1 = b1Insns.get(0); InsnNode i2 = b2Insns.get(0); if (i1.getArgsCount() != i2.getArgsCount()) { return false; } return i1.getArgsCount() == 0 || i1.getArg(0).equals(i2.getArg(0)); }
private static void renameVar(MethodNode mth, SSAVar[] vars, int[] vers, BlockNode block) { SSAVar[] inputVars = Arrays.copyOf(vars, vars.length); for (InsnNode insn : block.getInstructions()) { if (insn.getType() != InsnType.PHI) { for (InsnArg arg : insn.getArguments()) { if (!arg.isRegister()) { continue; } RegisterArg reg = (RegisterArg) arg; int regNum = reg.getRegNum(); SSAVar var = vars[regNum]; if (var == null) { throw new JadxRuntimeException( "Not initialized variable reg: " + regNum + ", insn: " + insn + ", block:" + block + ", method: " + mth); } var.use(reg); } } RegisterArg result = insn.getResult(); if (result != null) { int regNum = result.getRegNum(); vars[regNum] = mth.makeNewSVar(regNum, vers, result); } } for (BlockNode s : block.getSuccessors()) { PhiListAttr phiList = s.get(AType.PHI_LIST); if (phiList == null) { continue; } int j = s.getPredecessors().indexOf(block); if (j == -1) { throw new JadxRuntimeException("Can't find predecessor for " + block + " " + s); } for (PhiInsn phiInsn : phiList.getList()) { if (j >= phiInsn.getArgsCount()) { continue; } int regNum = phiInsn.getResult().getRegNum(); SSAVar var = vars[regNum]; if (var == null) { continue; } var.use(phiInsn.getArg(j)); var.setUsedInPhi(phiInsn); } } for (BlockNode domOn : block.getDominatesOn()) { renameVar(mth, vars, vers, domOn); } System.arraycopy(inputVars, 0, vars, 0, vars.length); }
private static void addPhi(BlockNode block, int regNum) { PhiListAttr phiList = block.get(AType.PHI_LIST); if (phiList == null) { phiList = new PhiListAttr(); block.addAttr(phiList); } PhiInsn phiInsn = new PhiInsn(regNum, block.getPredecessors().size()); phiList.getList().add(phiInsn); phiInsn.setOffset(block.getStartOffset()); block.getInstructions().add(0, phiInsn); }
/** Recursively traverse all blocks from 'block' until block from 'exits' */ private BlockNode traverse(IRegion r, BlockNode block, RegionStack stack) { BlockNode next = null; boolean processed = false; List<LoopInfo> loops = block.getAll(AType.LOOP); int loopCount = loops.size(); if (loopCount != 0 && block.contains(AFlag.LOOP_START)) { if (loopCount == 1) { next = processLoop(r, loops.get(0), stack); processed = true; } else { for (LoopInfo loop : loops) { if (loop.getStart() == block) { next = processLoop(r, loop, stack); processed = true; break; } } } } if (!processed && block.getInstructions().size() == 1) { InsnNode insn = block.getInstructions().get(0); switch (insn.getType()) { case IF: next = processIf(r, block, (IfNode) insn, stack); processed = true; break; case SWITCH: next = processSwitch(r, block, (SwitchNode) insn, stack); processed = true; break; case MONITOR_ENTER: next = processMonitorEnter(r, block, insn, stack); processed = true; break; default: break; } } if (!processed) { r.getSubBlocks().add(block); next = getNextBlock(block); } if (next != null && !stack.containsExit(block) && !stack.containsExit(next)) { return next; } return null; }
/** Select loop exit and construct LoopRegion */ private LoopRegion makeLoopRegion(IRegion curRegion, LoopInfo loop, List<BlockNode> exitBlocks) { for (BlockNode block : exitBlocks) { if (block.contains(AType.EXC_HANDLER) || block.getInstructions().size() != 1 || block.getInstructions().get(0).getType() != InsnType.IF) { continue; } List<LoopInfo> loops = block.getAll(AType.LOOP); if (!loops.isEmpty() && loops.get(0) != loop) { // skip nested loop condition continue; } LoopRegion loopRegion = new LoopRegion(curRegion, loop, block, block == loop.getEnd()); boolean found; if (block == loop.getStart() || block == loop.getEnd() || BlockUtils.isEmptySimplePath(loop.getStart(), block)) { found = true; } else if (block.getPredecessors().contains(loop.getStart())) { loopRegion.setPreCondition(loop.getStart()); // if we can't merge pre-condition this is not correct header found = loopRegion.checkPreCondition(); } else { found = false; } if (found) { List<LoopInfo> list = mth.getAllLoopsForBlock(block); if (list.size() >= 2) { // bad condition if successors going out of all loops boolean allOuter = true; for (BlockNode outerBlock : block.getCleanSuccessors()) { List<LoopInfo> outLoopList = mth.getAllLoopsForBlock(outerBlock); outLoopList.remove(loop); if (!outLoopList.isEmpty()) { // goes to outer loop allOuter = false; break; } } if (allOuter) { found = false; } } } if (found) { return loopRegion; } } // no exit found => endless loop return null; }
private static void insertContinue(LoopInfo loop) { BlockNode loopEnd = loop.getEnd(); List<BlockNode> predecessors = loopEnd.getPredecessors(); if (predecessors.size() <= 1) { return; } Set<BlockNode> loopExitNodes = loop.getExitNodes(); for (BlockNode pred : predecessors) { if (canInsertContinue(pred, predecessors, loopEnd, loopExitNodes)) { InsnNode cont = new InsnNode(InsnType.CONTINUE, 0); pred.getInstructions().add(cont); } } }
private static void insertContinueInSwitch(BlockNode block, BlockNode out, BlockNode end) { int endId = end.getId(); for (BlockNode s : block.getCleanSuccessors()) { if (s.getDomFrontier().get(endId) && s != out) { // search predecessor of loop end on path from this successor List<BlockNode> list = BlockUtils.collectBlocksDominatedBy(s, s); for (BlockNode p : end.getPredecessors()) { if (list.contains(p)) { if (p.isSynthetic()) { p.getInstructions().add(new InsnNode(InsnType.CONTINUE, 0)); } break; } } } } }
private boolean insertBreak(RegionStack stack, BlockNode loopExit, Edge exitEdge) { BlockNode exit = exitEdge.getTarget(); BlockNode insertBlock = null; boolean confirm = false; // process special cases if (loopExit == exit) { // try/catch at loop end BlockNode source = exitEdge.getSource(); if (source.contains(AType.CATCH_BLOCK) && source.getSuccessors().size() == 2) { BlockNode other = BlockUtils.selectOther(loopExit, source.getSuccessors()); if (other != null) { other = BlockUtils.skipSyntheticSuccessor(other); if (other.contains(AType.EXC_HANDLER)) { insertBlock = source; confirm = true; } } } } if (!confirm) { while (exit != null) { if (insertBlock != null && isPathExists(loopExit, exit)) { // found cross if (canInsertBreak(insertBlock)) { confirm = true; break; } return false; } insertBlock = exit; List<BlockNode> cs = exit.getCleanSuccessors(); exit = cs.size() == 1 ? cs.get(0) : null; } } if (!confirm) { return false; } InsnNode breakInsn = new InsnNode(InsnType.BREAK, 0); insertBlock.getInstructions().add(breakInsn); stack.addExit(exit); // add label to 'break' if needed addBreakLabel(exitEdge, exit, breakInsn); return true; }
/** Traverse from monitor-enter thru successors and collect blocks contains monitor-exit */ private static void traverseMonitorExits( SynchronizedRegion region, InsnArg arg, BlockNode block, Set<BlockNode> exits, Set<BlockNode> visited) { visited.add(block); for (InsnNode insn : block.getInstructions()) { if (insn.getType() == InsnType.MONITOR_EXIT && insn.getArg(0).equals(arg)) { exits.add(block); region.getExitInsns().add(insn); return; } } for (BlockNode node : block.getSuccessors()) { if (!visited.contains(node)) { traverseMonitorExits(region, arg, node, exits, visited); } } }
private BlockNode processLoop(IRegion curRegion, LoopInfo loop, RegionStack stack) { BlockNode loopStart = loop.getStart(); Set<BlockNode> exitBlocksSet = loop.getExitNodes(); // set exit blocks scan order priority // this can help if loop have several exits (after using 'break' or 'return' in loop) List<BlockNode> exitBlocks = new ArrayList<BlockNode>(exitBlocksSet.size()); BlockNode nextStart = getNextBlock(loopStart); if (nextStart != null && exitBlocksSet.remove(nextStart)) { exitBlocks.add(nextStart); } if (exitBlocksSet.remove(loopStart)) { exitBlocks.add(loopStart); } if (exitBlocksSet.remove(loop.getEnd())) { exitBlocks.add(loop.getEnd()); } exitBlocks.addAll(exitBlocksSet); LoopRegion loopRegion = makeLoopRegion(curRegion, loop, exitBlocks); if (loopRegion == null) { BlockNode exit = makeEndlessLoop(curRegion, stack, loop, loopStart); insertContinue(loop); return exit; } curRegion.getSubBlocks().add(loopRegion); IRegion outerRegion = stack.peekRegion(); stack.push(loopRegion); IfInfo condInfo = makeIfInfo(loopRegion.getHeader()); condInfo = searchNestedIf(condInfo); confirmMerge(condInfo); if (!loop.getLoopBlocks().contains(condInfo.getThenBlock())) { // invert loop condition if 'then' points to exit condInfo = IfInfo.invert(condInfo); } loopRegion.setCondition(condInfo.getCondition()); exitBlocks.removeAll(condInfo.getMergedBlocks()); if (!exitBlocks.isEmpty()) { BlockNode loopExit = condInfo.getElseBlock(); if (loopExit != null) { // add 'break' instruction before path cross between main loop exit and sub-exit for (Edge exitEdge : loop.getExitEdges()) { if (!exitBlocks.contains(exitEdge.getSource())) { continue; } insertBreak(stack, loopExit, exitEdge); } } } BlockNode out; if (loopRegion.isConditionAtEnd()) { BlockNode thenBlock = condInfo.getThenBlock(); out = thenBlock == loopStart ? condInfo.getElseBlock() : thenBlock; loopStart.remove(AType.LOOP); loop.getEnd().add(AFlag.SKIP); stack.addExit(loop.getEnd()); loopRegion.setBody(makeRegion(loopStart, stack)); loopStart.addAttr(AType.LOOP, loop); loop.getEnd().remove(AFlag.SKIP); } else { out = condInfo.getElseBlock(); if (outerRegion != null && out.contains(AFlag.LOOP_START) && !out.getAll(AType.LOOP).contains(loop) && RegionUtils.isRegionContainsBlock(outerRegion, out)) { // exit to already processed outer loop out = null; } stack.addExit(out); BlockNode loopBody = condInfo.getThenBlock(); Region body = makeRegion(loopBody, stack); // add blocks from loop start to first condition block BlockNode conditionBlock = condInfo.getIfBlock(); if (loopStart != conditionBlock) { Set<BlockNode> blocks = BlockUtils.getAllPathsBlocks(loopStart, conditionBlock); blocks.remove(conditionBlock); for (BlockNode block : blocks) { if (block.getInstructions().isEmpty() && !block.contains(AFlag.SKIP) && !RegionUtils.isRegionContainsBlock(body, block)) { body.add(block); } } } loopRegion.setBody(body); } stack.pop(); insertContinue(loop); return out; }