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 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 boolean canInsertBreak(BlockNode exit) { if (exit.contains(AFlag.RETURN) || BlockUtils.checkLastInsnType(exit, InsnType.BREAK)) { return false; } List<BlockNode> simplePath = BlockUtils.buildSimplePath(exit); if (!simplePath.isEmpty()) { BlockNode lastBlock = simplePath.get(simplePath.size() - 1); if (lastBlock.contains(AFlag.RETURN) || lastBlock.getSuccessors().isEmpty()) { return false; } } // check if there no outer switch (TODO: very expensive check) Set<BlockNode> paths = BlockUtils.getAllPathsBlocks(mth.getEnterBlock(), exit); for (BlockNode block : paths) { if (BlockUtils.checkLastInsnType(block, InsnType.SWITCH)) { return false; } } return true; }
public void processTryCatchBlocks(MethodNode mth) { Set<TryCatchBlock> tcs = new HashSet<TryCatchBlock>(); for (ExceptionHandler handler : mth.getExceptionHandlers()) { tcs.add(handler.getTryBlock()); } for (TryCatchBlock tc : tcs) { List<BlockNode> blocks = new ArrayList<BlockNode>(tc.getHandlersCount()); Set<BlockNode> splitters = new HashSet<BlockNode>(); for (ExceptionHandler handler : tc.getHandlers()) { BlockNode handlerBlock = handler.getHandlerBlock(); if (handlerBlock != null) { blocks.add(handlerBlock); splitters.addAll(handlerBlock.getPredecessors()); } else { LOG.debug(ErrorsCounter.formatErrorMsg(mth, "No exception handler block: " + handler)); } } Set<BlockNode> exits = new HashSet<BlockNode>(); for (BlockNode splitter : splitters) { for (BlockNode handler : blocks) { List<BlockNode> s = splitter.getSuccessors(); if (s.isEmpty()) { LOG.debug(ErrorsCounter.formatErrorMsg(mth, "No successors for splitter: " + splitter)); continue; } BlockNode ss = s.get(0); BlockNode cross = BlockUtils.getPathCross(mth, ss, handler); if (cross != null && cross != ss && cross != handler) { exits.add(cross); } } } for (ExceptionHandler handler : tc.getHandlers()) { processExcHandler(handler, exits); } } }
private BlockNode processSwitch( IRegion currentRegion, BlockNode block, SwitchNode insn, RegionStack stack) { SwitchRegion sw = new SwitchRegion(currentRegion, block); currentRegion.getSubBlocks().add(sw); int len = insn.getTargets().length; // sort by target Map<Integer, List<Object>> casesMap = new LinkedHashMap<Integer, List<Object>>(len); for (int i = 0; i < len; i++) { Object key = insn.getKeys()[i]; int targ = insn.getTargets()[i]; List<Object> keys = casesMap.get(targ); if (keys == null) { keys = new ArrayList<Object>(2); casesMap.put(targ, keys); } keys.add(key); } Map<BlockNode, List<Object>> blocksMap = new LinkedHashMap<BlockNode, List<Object>>(len); for (Map.Entry<Integer, List<Object>> entry : casesMap.entrySet()) { BlockNode c = getBlockByOffset(entry.getKey(), block.getSuccessors()); assert c != null; blocksMap.put(c, entry.getValue()); } BlockNode defCase = getBlockByOffset(insn.getDefaultCaseOffset(), block.getSuccessors()); if (defCase != null) { blocksMap.remove(defCase); } LoopInfo loop = mth.getLoopForBlock(block); Map<BlockNode, BlockNode> fallThroughCases = new LinkedHashMap<BlockNode, BlockNode>(); List<BlockNode> basicBlocks = mth.getBasicBlocks(); BitSet outs = new BitSet(basicBlocks.size()); outs.or(block.getDomFrontier()); for (BlockNode s : block.getCleanSuccessors()) { BitSet df = s.getDomFrontier(); // fall through case block if (df.cardinality() > 1) { if (df.cardinality() > 2) { LOG.debug("Unexpected case pattern, block: {}, mth: {}", s, mth); } else { BlockNode first = basicBlocks.get(df.nextSetBit(0)); BlockNode second = basicBlocks.get(df.nextSetBit(first.getId() + 1)); if (second.getDomFrontier().get(first.getId())) { fallThroughCases.put(s, second); df = new BitSet(df.size()); df.set(first.getId()); } else if (first.getDomFrontier().get(second.getId())) { fallThroughCases.put(s, first); df = new BitSet(df.size()); df.set(second.getId()); } } } outs.or(df); } outs.clear(block.getId()); if (loop != null) { outs.clear(loop.getStart().getId()); } stack.push(sw); stack.addExits(BlockUtils.bitSetToBlocks(mth, outs)); // check cases order if fall through case exists if (!fallThroughCases.isEmpty()) { if (isBadCasesOrder(blocksMap, fallThroughCases)) { LOG.debug("Fixing incorrect switch cases order, method: {}", mth); blocksMap = reOrderSwitchCases(blocksMap, fallThroughCases); if (isBadCasesOrder(blocksMap, fallThroughCases)) { LOG.error("Can't fix incorrect switch cases order, method: {}", mth); mth.add(AFlag.INCONSISTENT_CODE); } } } // filter 'out' block if (outs.cardinality() > 1) { // remove exception handlers BlockUtils.cleanBitSet(mth, outs); } if (outs.cardinality() > 1) { // filter loop start and successors of other blocks for (int i = outs.nextSetBit(0); i >= 0; i = outs.nextSetBit(i + 1)) { BlockNode b = basicBlocks.get(i); outs.andNot(b.getDomFrontier()); if (b.contains(AFlag.LOOP_START)) { outs.clear(b.getId()); } else { for (BlockNode s : b.getCleanSuccessors()) { outs.clear(s.getId()); } } } } if (loop != null && outs.cardinality() > 1) { outs.clear(loop.getEnd().getId()); } if (outs.cardinality() == 0) { // one or several case blocks are empty, // run expensive algorithm for find 'out' block for (BlockNode maybeOut : block.getSuccessors()) { boolean allReached = true; for (BlockNode s : block.getSuccessors()) { if (!isPathExists(s, maybeOut)) { allReached = false; break; } } if (allReached) { outs.set(maybeOut.getId()); break; } } } BlockNode out = null; if (outs.cardinality() == 1) { out = basicBlocks.get(outs.nextSetBit(0)); stack.addExit(out); } else if (loop == null && outs.cardinality() > 1) { LOG.warn("Can't detect out node for switch block: {} in {}", block, mth); } if (loop != null) { // check if 'continue' must be inserted BlockNode end = loop.getEnd(); if (out != end && out != null) { insertContinueInSwitch(block, out, end); } } if (!stack.containsExit(defCase)) { sw.setDefaultCase(makeRegion(defCase, stack)); } for (Entry<BlockNode, List<Object>> entry : blocksMap.entrySet()) { BlockNode caseBlock = entry.getKey(); if (stack.containsExit(caseBlock)) { // empty case block sw.addCase(entry.getValue(), new Region(stack.peekRegion())); } else { BlockNode next = fallThroughCases.get(caseBlock); stack.addExit(next); Region caseRegion = makeRegion(caseBlock, stack); stack.removeExit(next); if (next != null) { next.add(AFlag.FALL_THROUGH); caseRegion.add(AFlag.FALL_THROUGH); } sw.addCase(entry.getValue(), caseRegion); // 'break' instruction will be inserted in RegionMakerVisitor.PostRegionVisitor } } stack.pop(); return out; }