Search in sources :

Example 1 with LoopInfo

use of jadx.core.dex.attributes.nodes.LoopInfo in project jadx by skylot.

the class RegionMaker method processSwitch.

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());
        if (c == null) {
            throw new JadxRuntimeException("Switch block not found by offset: " + entry.getKey());
        }
        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) {
        // 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;
}
Also used : BlockNode(jadx.core.dex.nodes.BlockNode) BitSet(java.util.BitSet) LinkedHashMap(java.util.LinkedHashMap) LoopInfo(jadx.core.dex.attributes.nodes.LoopInfo) Region(jadx.core.dex.regions.Region) IRegion(jadx.core.dex.nodes.IRegion) SwitchRegion(jadx.core.dex.regions.SwitchRegion) SynchronizedRegion(jadx.core.dex.regions.SynchronizedRegion) LoopRegion(jadx.core.dex.regions.loops.LoopRegion) IfRegion(jadx.core.dex.regions.conditions.IfRegion) SwitchRegion(jadx.core.dex.regions.SwitchRegion) List(java.util.List) ArrayList(java.util.ArrayList) JadxRuntimeException(jadx.core.utils.exceptions.JadxRuntimeException) Map(java.util.Map) LinkedHashMap(java.util.LinkedHashMap)

Example 2 with LoopInfo

use of jadx.core.dex.attributes.nodes.LoopInfo in project jadx by skylot.

the class BlockProcessor method processNestedLoops.

private static void processNestedLoops(MethodNode mth) {
    if (mth.getLoopsCount() == 0) {
        return;
    }
    for (LoopInfo outLoop : mth.getLoops()) {
        for (LoopInfo innerLoop : mth.getLoops()) {
            if (outLoop == innerLoop) {
                continue;
            }
            if (outLoop.getLoopBlocks().containsAll(innerLoop.getLoopBlocks())) {
                LoopInfo parentLoop = innerLoop.getParentLoop();
                if (parentLoop != null) {
                    if (parentLoop.getLoopBlocks().containsAll(outLoop.getLoopBlocks())) {
                        outLoop.setParentLoop(parentLoop);
                        innerLoop.setParentLoop(outLoop);
                    } else {
                        parentLoop.setParentLoop(outLoop);
                    }
                } else {
                    innerLoop.setParentLoop(outLoop);
                }
            }
        }
    }
}
Also used : LoopInfo(jadx.core.dex.attributes.nodes.LoopInfo)

Example 3 with LoopInfo

use of jadx.core.dex.attributes.nodes.LoopInfo in project jadx by skylot.

the class BlockProcessor method modifyBlocksTree.

private static boolean modifyBlocksTree(MethodNode mth) {
    for (BlockNode block : mth.getBasicBlocks()) {
        if (block.getPredecessors().isEmpty() && block != mth.getEnterBlock()) {
            throw new JadxRuntimeException("Unreachable block: " + block);
        }
        // check loops
        List<LoopInfo> loops = block.getAll(AType.LOOP);
        if (loops.size() > 1) {
            boolean oneHeader = true;
            for (LoopInfo loop : loops) {
                if (loop.getStart() != block) {
                    oneHeader = false;
                    break;
                }
            }
            if (oneHeader) {
                // several back edges connected to one loop header => make additional block
                BlockNode newLoopHeader = BlockSplitter.startNewBlock(mth, block.getStartOffset());
                newLoopHeader.add(AFlag.SYNTHETIC);
                connect(newLoopHeader, block);
                for (LoopInfo la : loops) {
                    BlockNode node = la.getEnd();
                    removeConnection(node, block);
                    connect(node, newLoopHeader);
                }
                return true;
            }
        }
        if (loops.size() == 1) {
            LoopInfo loop = loops.get(0);
            // insert additional blocks for possible 'break' insertion
            List<Edge> edges = loop.getExitEdges();
            if (!edges.isEmpty()) {
                boolean change = false;
                for (Edge edge : edges) {
                    BlockNode target = edge.getTarget();
                    if (!target.contains(AFlag.SYNTHETIC)) {
                        BlockSplitter.insertBlockBetween(mth, edge.getSource(), target);
                        change = true;
                    }
                }
                if (change) {
                    return true;
                }
            }
            // insert additional blocks for possible 'continue' insertion
            BlockNode loopEnd = loop.getEnd();
            if (loopEnd.getPredecessors().size() > 1) {
                boolean change = false;
                List<BlockNode> nodes = new ArrayList<BlockNode>(loopEnd.getPredecessors());
                for (BlockNode pred : nodes) {
                    if (!pred.contains(AFlag.SYNTHETIC)) {
                        BlockSplitter.insertBlockBetween(mth, pred, loopEnd);
                        change = true;
                    }
                }
                if (change) {
                    return true;
                }
            }
        }
    }
    return splitReturn(mth);
}
Also used : BlockNode(jadx.core.dex.nodes.BlockNode) LoopInfo(jadx.core.dex.attributes.nodes.LoopInfo) ArrayList(java.util.ArrayList) JadxRuntimeException(jadx.core.utils.exceptions.JadxRuntimeException) Edge(jadx.core.dex.nodes.Edge)

Example 4 with LoopInfo

use of jadx.core.dex.attributes.nodes.LoopInfo in project jadx by skylot.

the class BlockNode method cleanSuccessors.

/**
 * Return all successor which are not exception handler or followed by loop back edge
 */
private static List<BlockNode> cleanSuccessors(BlockNode block) {
    List<BlockNode> sucList = block.getSuccessors();
    if (sucList.isEmpty()) {
        return sucList;
    }
    List<BlockNode> toRemove = new ArrayList<>(sucList.size());
    for (BlockNode b : sucList) {
        if (BlockUtils.isExceptionHandlerPath(b)) {
            toRemove.add(b);
        }
    }
    if (block.contains(AFlag.LOOP_END)) {
        List<LoopInfo> loops = block.getAll(AType.LOOP);
        for (LoopInfo loop : loops) {
            toRemove.add(loop.getStart());
        }
    }
    if (toRemove.isEmpty()) {
        return sucList;
    }
    List<BlockNode> result = new ArrayList<>(sucList);
    result.removeAll(toRemove);
    return result;
}
Also used : LoopInfo(jadx.core.dex.attributes.nodes.LoopInfo) ArrayList(java.util.ArrayList)

Example 5 with LoopInfo

use of jadx.core.dex.attributes.nodes.LoopInfo in project jadx by skylot.

the class RegionMaker method addBreakLabel.

private void addBreakLabel(Edge exitEdge, BlockNode exit, InsnNode breakInsn) {
    BlockNode outBlock = BlockUtils.getNextBlock(exitEdge.getTarget());
    if (outBlock == null) {
        return;
    }
    List<LoopInfo> exitLoop = mth.getAllLoopsForBlock(outBlock);
    if (!exitLoop.isEmpty()) {
        return;
    }
    List<LoopInfo> inLoops = mth.getAllLoopsForBlock(exitEdge.getSource());
    if (inLoops.size() < 2) {
        return;
    }
    // search for parent loop
    LoopInfo parentLoop = null;
    for (LoopInfo loop : inLoops) {
        if (loop.getParentLoop() == null) {
            parentLoop = loop;
            break;
        }
    }
    if (parentLoop == null) {
        return;
    }
    if (parentLoop.getEnd() != exit && !parentLoop.getExitNodes().contains(exit)) {
        LoopLabelAttr labelAttr = new LoopLabelAttr(parentLoop);
        breakInsn.addAttr(labelAttr);
        parentLoop.getStart().addAttr(labelAttr);
    }
}
Also used : BlockNode(jadx.core.dex.nodes.BlockNode) LoopInfo(jadx.core.dex.attributes.nodes.LoopInfo) LoopLabelAttr(jadx.core.dex.attributes.nodes.LoopLabelAttr)

Aggregations

LoopInfo (jadx.core.dex.attributes.nodes.LoopInfo)17 BlockNode (jadx.core.dex.nodes.BlockNode)12 InsnNode (jadx.core.dex.nodes.InsnNode)5 ArrayList (java.util.ArrayList)5 LoopRegion (jadx.core.dex.regions.loops.LoopRegion)4 JadxRuntimeException (jadx.core.utils.exceptions.JadxRuntimeException)4 LoopLabelAttr (jadx.core.dex.attributes.nodes.LoopLabelAttr)3 Edge (jadx.core.dex.nodes.Edge)3 IRegion (jadx.core.dex.nodes.IRegion)3 Region (jadx.core.dex.regions.Region)3 SwitchRegion (jadx.core.dex.regions.SwitchRegion)3 SynchronizedRegion (jadx.core.dex.regions.SynchronizedRegion)3 IfRegion (jadx.core.dex.regions.conditions.IfRegion)3 IfNode (jadx.core.dex.instructions.IfNode)2 InsnType (jadx.core.dex.instructions.InsnType)2 SwitchInsn (jadx.core.dex.instructions.SwitchInsn)2 ExceptionHandler (jadx.core.dex.trycatch.ExceptionHandler)2 BitSet (java.util.BitSet)2 LinkedHashMap (java.util.LinkedHashMap)2 List (java.util.List)2