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;
}
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;
}
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;
}
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;
}
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;
}
Aggregations