Search in sources :

Example 1 with SwitchRegion

use of jadx.core.dex.regions.SwitchRegion 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 SwitchRegion

use of jadx.core.dex.regions.SwitchRegion in project jadx by skylot.

the class RegionMakerVisitor method processSwitch.

private static void processSwitch(MethodNode mth, SwitchRegion sw) {
    for (IContainer c : sw.getBranches()) {
        if (!(c instanceof Region)) {
            continue;
        }
        Set<IBlock> blocks = new HashSet<IBlock>();
        RegionUtils.getAllRegionBlocks(c, blocks);
        if (blocks.isEmpty()) {
            addBreakToContainer((Region) c);
            continue;
        }
        for (IBlock block : blocks) {
            if (!(block instanceof BlockNode)) {
                continue;
            }
            BlockNode bn = (BlockNode) block;
            for (BlockNode s : bn.getCleanSuccessors()) {
                if (!blocks.contains(s) && !bn.contains(AFlag.SKIP) && !s.contains(AFlag.FALL_THROUGH)) {
                    addBreak(mth, c, bn);
                    break;
                }
            }
        }
    }
}
Also used : BlockNode(jadx.core.dex.nodes.BlockNode) IBlock(jadx.core.dex.nodes.IBlock) 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) Region(jadx.core.dex.regions.Region) IContainer(jadx.core.dex.nodes.IContainer) HashSet(java.util.HashSet)

Aggregations

BlockNode (jadx.core.dex.nodes.BlockNode)2 IRegion (jadx.core.dex.nodes.IRegion)2 Region (jadx.core.dex.regions.Region)2 SwitchRegion (jadx.core.dex.regions.SwitchRegion)2 SynchronizedRegion (jadx.core.dex.regions.SynchronizedRegion)2 LoopRegion (jadx.core.dex.regions.loops.LoopRegion)2 LoopInfo (jadx.core.dex.attributes.nodes.LoopInfo)1 IBlock (jadx.core.dex.nodes.IBlock)1 IContainer (jadx.core.dex.nodes.IContainer)1 IfRegion (jadx.core.dex.regions.conditions.IfRegion)1 JadxRuntimeException (jadx.core.utils.exceptions.JadxRuntimeException)1 ArrayList (java.util.ArrayList)1 BitSet (java.util.BitSet)1 HashSet (java.util.HashSet)1 LinkedHashMap (java.util.LinkedHashMap)1 List (java.util.List)1 Map (java.util.Map)1