Search in sources :

Example 11 with IRegion

use of jadx.core.dex.nodes.IRegion in project jadx by skylot.

the class RegionMaker method makeEndlessLoop.

private BlockNode makeEndlessLoop(IRegion curRegion, RegionStack stack, LoopInfo loop, BlockNode loopStart) {
    LoopRegion loopRegion = new LoopRegion(curRegion, loop, null, false);
    curRegion.getSubBlocks().add(loopRegion);
    loopStart.remove(AType.LOOP);
    processedBlocks.clear(loopStart.getId());
    stack.push(loopRegion);
    BlockNode out = null;
    // insert 'break' for exits
    List<Edge> exitEdges = loop.getExitEdges();
    if (exitEdges.size() == 1) {
        Edge exitEdge = exitEdges.get(0);
        BlockNode exit = exitEdge.getTarget();
        if (insertLoopBreak(stack, loop, exit, exitEdge)) {
            BlockNode nextBlock = getNextBlock(exit);
            if (nextBlock != null) {
                stack.addExit(nextBlock);
                out = nextBlock;
            }
        }
    } else {
        for (Edge exitEdge : exitEdges) {
            BlockNode exit = exitEdge.getTarget();
            List<BlockNode> blocks = BlockUtils.bitSetToBlocks(mth, exit.getDomFrontier());
            for (BlockNode block : blocks) {
                if (BlockUtils.isPathExists(exit, block)) {
                    stack.addExit(block);
                    insertLoopBreak(stack, loop, block, exitEdge);
                    out = block;
                } else {
                    insertLoopBreak(stack, loop, exit, exitEdge);
                }
            }
        }
    }
    Region body = makeRegion(loopStart, stack);
    BlockNode loopEnd = loop.getEnd();
    if (!RegionUtils.isRegionContainsBlock(body, loopEnd) && !loopEnd.contains(AType.EXC_HANDLER) && !inExceptionHandlerBlocks(loopEnd)) {
        body.getSubBlocks().add(loopEnd);
    }
    loopRegion.setBody(body);
    if (out == null) {
        BlockNode next = getNextBlock(loopEnd);
        out = RegionUtils.isRegionContainsBlock(body, next) ? null : next;
    }
    stack.pop();
    loopStart.addAttr(AType.LOOP, loop);
    return out;
}
Also used : BlockNode(jadx.core.dex.nodes.BlockNode) 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) LoopRegion(jadx.core.dex.regions.loops.LoopRegion) Edge(jadx.core.dex.nodes.Edge)

Example 12 with IRegion

use of jadx.core.dex.nodes.IRegion in project jadx by skylot.

the class RegionMaker method processIf.

private BlockNode processIf(IRegion currentRegion, BlockNode block, IfNode ifnode, RegionStack stack) {
    if (block.contains(AFlag.ADDED_TO_REGION)) {
        // block already included in other 'if' region
        return ifnode.getThenBlock();
    }
    IfInfo currentIf = makeIfInfo(mth, block);
    if (currentIf == null) {
        return null;
    }
    IfInfo mergedIf = mergeNestedIfNodes(currentIf);
    if (mergedIf != null) {
        currentIf = mergedIf;
    } else {
        // invert simple condition (compiler often do it)
        currentIf = IfInfo.invert(currentIf);
    }
    IfInfo modifiedIf = IfMakerHelper.restructureIf(mth, block, currentIf);
    if (modifiedIf != null) {
        currentIf = modifiedIf;
    } else {
        if (currentIf.getMergedBlocks().size() <= 1) {
            return null;
        }
        currentIf = makeIfInfo(mth, block);
        currentIf = IfMakerHelper.restructureIf(mth, block, currentIf);
        if (currentIf == null) {
            // all attempts failed
            return null;
        }
    }
    confirmMerge(currentIf);
    IfRegion ifRegion = new IfRegion(currentRegion);
    ifRegion.updateCondition(currentIf);
    currentRegion.getSubBlocks().add(ifRegion);
    BlockNode outBlock = currentIf.getOutBlock();
    stack.push(ifRegion);
    stack.addExit(outBlock);
    ifRegion.setThenRegion(makeRegion(currentIf.getThenBlock(), stack));
    BlockNode elseBlock = currentIf.getElseBlock();
    if (elseBlock == null || stack.containsExit(elseBlock)) {
        ifRegion.setElseRegion(null);
    } else {
        ifRegion.setElseRegion(makeRegion(elseBlock, stack));
    }
    // TODO: make more common algorithm
    if (ifRegion.getElseRegion() == null && outBlock != null) {
        List<EdgeInsnAttr> edgeInsnAttrs = outBlock.getAll(AType.EDGE_INSN);
        if (!edgeInsnAttrs.isEmpty()) {
            Region elseRegion = new Region(ifRegion);
            for (EdgeInsnAttr edgeInsnAttr : edgeInsnAttrs) {
                if (edgeInsnAttr.getEnd().equals(outBlock)) {
                    addEdgeInsn(currentIf, elseRegion, edgeInsnAttr);
                }
            }
            ifRegion.setElseRegion(elseRegion);
        }
    }
    stack.pop();
    return outBlock;
}
Also used : BlockNode(jadx.core.dex.nodes.BlockNode) 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) IfMakerHelper.makeIfInfo(jadx.core.dex.visitors.regions.IfMakerHelper.makeIfInfo) IfInfo(jadx.core.dex.regions.conditions.IfInfo) IfRegion(jadx.core.dex.regions.conditions.IfRegion) EdgeInsnAttr(jadx.core.dex.attributes.nodes.EdgeInsnAttr)

Example 13 with IRegion

use of jadx.core.dex.nodes.IRegion in project jadx by skylot.

the class RegionMaker method processHandlersOutBlocks.

/**
 * Search handlers successor blocks not included in any region.
 */
protected IRegion processHandlersOutBlocks(MethodNode mth, List<TryCatchBlockAttr> tcs) {
    Set<IBlock> allRegionBlocks = new HashSet<>();
    RegionUtils.getAllRegionBlocks(mth.getRegion(), allRegionBlocks);
    Set<IBlock> succBlocks = new HashSet<>();
    for (TryCatchBlockAttr tc : tcs) {
        for (ExceptionHandler handler : tc.getHandlers()) {
            IContainer region = handler.getHandlerRegion();
            if (region != null) {
                IBlock lastBlock = RegionUtils.getLastBlock(region);
                if (lastBlock instanceof BlockNode) {
                    succBlocks.addAll(((BlockNode) lastBlock).getSuccessors());
                }
                RegionUtils.getAllRegionBlocks(region, allRegionBlocks);
            }
        }
    }
    succBlocks.removeAll(allRegionBlocks);
    if (succBlocks.isEmpty()) {
        return null;
    }
    Region excOutRegion = new Region(mth.getRegion());
    for (IBlock block : succBlocks) {
        if (block instanceof BlockNode) {
            excOutRegion.add(makeRegion((BlockNode) block, new RegionStack(mth)));
        }
    }
    return excOutRegion;
}
Also used : ExceptionHandler(jadx.core.dex.trycatch.ExceptionHandler) BlockNode(jadx.core.dex.nodes.BlockNode) IBlock(jadx.core.dex.nodes.IBlock) TryCatchBlockAttr(jadx.core.dex.trycatch.TryCatchBlockAttr) 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) IContainer(jadx.core.dex.nodes.IContainer) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet)

Example 14 with IRegion

use of jadx.core.dex.nodes.IRegion in project jadx by skylot.

the class RegionMaker method processLoop.

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<>(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(mth, 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.updateCondition(condInfo);
    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())) {
                    insertLoopBreak(stack, loop, 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.ADDED_TO_REGION);
        stack.addExit(loop.getEnd());
        processedBlocks.clear(loopStart.getId());
        Region body = makeRegion(loopStart, stack);
        loopRegion.setBody(body);
        loopStart.addAttr(AType.LOOP, loop);
        loop.getEnd().remove(AFlag.ADDED_TO_REGION);
    } 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;
        if (Objects.equals(loopBody, loopStart)) {
            // empty loop body
            body = new Region(loopRegion);
        } else {
            body = makeRegion(loopBody, stack);
        }
        // add blocks from loop start to first condition block
        BlockNode conditionBlock = condInfo.getFirstIfBlock();
        if (loopStart != conditionBlock) {
            Set<BlockNode> blocks = BlockUtils.getAllPathsBlocks(loopStart, conditionBlock);
            blocks.remove(conditionBlock);
            for (BlockNode block : blocks) {
                if (block.getInstructions().isEmpty() && !block.contains(AFlag.ADDED_TO_REGION) && !RegionUtils.isRegionContainsBlock(body, block)) {
                    body.add(block);
                }
            }
        }
        loopRegion.setBody(body);
    }
    stack.pop();
    insertContinue(loop);
    return out;
}
Also used : BlockNode(jadx.core.dex.nodes.BlockNode) ArrayList(java.util.ArrayList) 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) IfMakerHelper.makeIfInfo(jadx.core.dex.visitors.regions.IfMakerHelper.makeIfInfo) IfInfo(jadx.core.dex.regions.conditions.IfInfo) LoopRegion(jadx.core.dex.regions.loops.LoopRegion) Edge(jadx.core.dex.nodes.Edge) IRegion(jadx.core.dex.nodes.IRegion)

Example 15 with IRegion

use of jadx.core.dex.nodes.IRegion in project jadx by skylot.

the class RegionMaker method processSwitch.

private BlockNode processSwitch(IRegion currentRegion, BlockNode block, SwitchInsn insn, RegionStack stack) {
    // map case blocks to keys
    int len = insn.getTargets().length;
    Map<BlockNode, List<Object>> blocksMap = new LinkedHashMap<>(len);
    BlockNode[] targetBlocksArr = insn.getTargetBlocks();
    for (int i = 0; i < len; i++) {
        List<Object> keys = blocksMap.computeIfAbsent(targetBlocksArr[i], k -> new ArrayList<>(2));
        keys.add(insn.getKey(i));
    }
    BlockNode defCase = insn.getDefTargetBlock();
    if (defCase != null) {
        List<Object> keys = blocksMap.computeIfAbsent(defCase, k -> new ArrayList<>(1));
        keys.add(SwitchRegion.DEFAULT_CASE_KEY);
    }
    // search 'out' block - 'next' block after whole switch statement
    BlockNode out;
    LoopInfo loop = mth.getLoopForBlock(block);
    if (loop == null) {
        out = calcPostDomOut(mth, block, mth.getPreExitBlocks());
    } else {
        BlockNode loopEnd = loop.getEnd();
        stack.addExit(loop.getStart());
        if (stack.containsExit(block) || block == loopEnd || loopEnd.getPredecessors().contains(block)) {
            // in exits or last insn in loop => no 'out' block
            out = null;
        } else {
            // treat 'continue' as exit
            out = calcPostDomOut(mth, block, loopEnd.getPredecessors());
            if (out != null) {
                insertContinueInSwitch(block, out, loopEnd);
            } else {
                // no 'continue'
                out = calcPostDomOut(mth, block, Collections.singletonList(loopEnd));
            }
        }
        if (out == loop.getStart()) {
            // no other outs instead back edge to loop start
            out = null;
        }
    }
    if (out != null && processedBlocks.get(out.getId())) {
        // out block already processed, prevent endless loop
        throw new JadxRuntimeException("Failed to find switch 'out' block");
    }
    SwitchRegion sw = new SwitchRegion(currentRegion, block);
    currentRegion.getSubBlocks().add(sw);
    stack.push(sw);
    stack.addExit(out);
    // detect fallthrough cases
    Map<BlockNode, BlockNode> fallThroughCases = new LinkedHashMap<>();
    if (out != null) {
        BitSet caseBlocks = BlockUtils.blocksToBitSet(mth, blocksMap.keySet());
        caseBlocks.clear(out.getId());
        for (BlockNode successor : block.getCleanSuccessors()) {
            BlockNode fallThroughBlock = searchFallThroughCase(successor, out, caseBlocks);
            if (fallThroughBlock != null) {
                fallThroughCases.put(successor, fallThroughBlock);
            }
        }
        // check fallthrough cases order
        if (!fallThroughCases.isEmpty() && isBadCasesOrder(blocksMap, fallThroughCases)) {
            Map<BlockNode, List<Object>> newBlocksMap = reOrderSwitchCases(blocksMap, fallThroughCases);
            if (isBadCasesOrder(newBlocksMap, fallThroughCases)) {
                mth.addWarnComment("Can't fix incorrect switch cases order, some code will duplicate");
                fallThroughCases.clear();
            } else {
                blocksMap = newBlocksMap;
            }
        }
    }
    for (Entry<BlockNode, List<Object>> entry : blocksMap.entrySet()) {
        List<Object> keysList = entry.getValue();
        BlockNode caseBlock = entry.getKey();
        if (stack.containsExit(caseBlock)) {
            sw.addCase(keysList, 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(keysList, caseRegion);
        // 'break' instruction will be inserted in RegionMakerVisitor.PostRegionVisitor
        }
    }
    removeEmptyCases(insn, sw, defCase);
    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) List(java.util.List) ArrayList(java.util.ArrayList) JadxRuntimeException(jadx.core.utils.exceptions.JadxRuntimeException) SwitchRegion(jadx.core.dex.regions.SwitchRegion)

Aggregations

IRegion (jadx.core.dex.nodes.IRegion)26 IContainer (jadx.core.dex.nodes.IContainer)14 Region (jadx.core.dex.regions.Region)12 LoopRegion (jadx.core.dex.regions.loops.LoopRegion)11 BlockNode (jadx.core.dex.nodes.BlockNode)10 IBlock (jadx.core.dex.nodes.IBlock)10 IfRegion (jadx.core.dex.regions.conditions.IfRegion)8 IBranchRegion (jadx.core.dex.nodes.IBranchRegion)7 SwitchRegion (jadx.core.dex.regions.SwitchRegion)7 SynchronizedRegion (jadx.core.dex.regions.SynchronizedRegion)7 JadxRuntimeException (jadx.core.utils.exceptions.JadxRuntimeException)5 HashSet (java.util.HashSet)4 InsnNode (jadx.core.dex.nodes.InsnNode)3 MethodNode (jadx.core.dex.nodes.MethodNode)3 ArrayList (java.util.ArrayList)3 LinkedHashMap (java.util.LinkedHashMap)3 LoopInfo (jadx.core.dex.attributes.nodes.LoopInfo)2 Edge (jadx.core.dex.nodes.Edge)2 AbstractRegion (jadx.core.dex.regions.AbstractRegion)2 TryCatchRegion (jadx.core.dex.regions.TryCatchRegion)2