use of jadx.core.dex.nodes.Edge 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);
}
use of jadx.core.dex.nodes.Edge 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.Edge 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.Edge in project jadx by skylot.
the class BlockProcessor method insertBlocksForBreak.
/**
* Insert additional blocks for possible 'break' insertion
*/
private static boolean insertBlocksForBreak(MethodNode mth, LoopInfo loop) {
boolean change = false;
List<Edge> edges = loop.getExitEdges();
if (!edges.isEmpty()) {
for (Edge edge : edges) {
BlockNode target = edge.getTarget();
BlockNode source = edge.getSource();
if (!target.contains(AFlag.SYNTHETIC) && !source.contains(AFlag.SYNTHETIC)) {
BlockSplitter.insertBlockBetween(mth, source, target);
change = true;
}
}
}
return change;
}
use of jadx.core.dex.nodes.Edge in project jadx by skylot.
the class RegionMaker method checkLoopExits.
private boolean checkLoopExits(LoopInfo loop, BlockNode mainExitBlock) {
List<Edge> exitEdges = loop.getExitEdges();
if (exitEdges.size() < 2) {
return true;
}
Optional<Edge> mainEdgeOpt = exitEdges.stream().filter(edge -> edge.getSource() == mainExitBlock).findFirst();
if (!mainEdgeOpt.isPresent()) {
throw new JadxRuntimeException("Not found exit edge by exit block: " + mainExitBlock);
}
Edge mainExitEdge = mainEdgeOpt.get();
BlockNode mainOutBlock = mainExitEdge.getTarget();
for (Edge exitEdge : exitEdges) {
if (exitEdge != mainExitEdge) {
// all exit paths must be same or don't cross (will be inside loop)
BlockNode exitBlock = exitEdge.getTarget();
if (!isEqualPaths(mainOutBlock, exitBlock)) {
BlockNode crossBlock = BlockUtils.getPathCross(mth, mainOutBlock, exitBlock);
if (crossBlock != null) {
return false;
}
}
}
}
return true;
}
Aggregations