package jadx.core.dex.visitors.regions;
import jadx.core.Consts;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.LoopInfo;
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
import jadx.core.dex.instructions.IfNode;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.SwitchNode;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.Edge;
import jadx.core.dex.nodes.IRegion;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.regions.Region;
import jadx.core.dex.regions.SwitchRegion;
import jadx.core.dex.regions.SynchronizedRegion;
import jadx.core.dex.regions.conditions.IfInfo;
import jadx.core.dex.regions.conditions.IfRegion;
import jadx.core.dex.regions.loops.LoopRegion;
import jadx.core.dex.trycatch.ExcHandlerAttr;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.TryCatchBlock;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.InstructionRemover;
import jadx.core.utils.RegionUtils;
import jadx.core.utils.exceptions.JadxOverflowException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static jadx.core.dex.visitors.regions.IfMakerHelper.confirmMerge;
import static jadx.core.dex.visitors.regions.IfMakerHelper.makeIfInfo;
import static jadx.core.dex.visitors.regions.IfMakerHelper.mergeNestedIfNodes;
import static jadx.core.dex.visitors.regions.IfMakerHelper.searchNestedIf;
import static jadx.core.utils.BlockUtils.getBlockByOffset;
import static jadx.core.utils.BlockUtils.getNextBlock;
import static jadx.core.utils.BlockUtils.isPathExists;
public class RegionMaker {
private static final Logger LOG = LoggerFactory.getLogger(RegionMaker.class);
// 'dumb' guard to prevent endless loop in regions processing
private static final int REGIONS_LIMIT = 1000 * 1000;
private final MethodNode mth;
private BitSet processedBlocks;
private int regionsCount;
public RegionMaker(MethodNode mth) {
this.mth = mth;
if (Consts.DEBUG) {
this.processedBlocks = new BitSet(mth.getBasicBlocks().size());
}
}
public Region makeRegion(BlockNode startBlock, RegionStack stack) {
if (Consts.DEBUG) {
int id = startBlock.getId();
if (processedBlocks.get(id)) {
LOG.debug(" Block already processed: {}, mth: {}", startBlock, mth);
} else {
processedBlocks.set(id);
}
}
regionsCount++;
if (regionsCount > REGIONS_LIMIT) {
throw new JadxOverflowException("Regions count limit reached");
}
Region r = new Region(stack.peekRegion());
BlockNode next = startBlock;
while (next != null) {
next = traverse(r, next, stack);
}
return r;
}
/**
* Recursively traverse all blocks from 'block' until block from 'exits'
*/
private BlockNode traverse(IRegion r, BlockNode block, RegionStack stack) {
BlockNode next = null;
boolean processed = false;
List<LoopInfo> loops = block.getAll(AType.LOOP);
int loopCount = loops.size();
if (loopCount != 0 && block.contains(AFlag.LOOP_START)) {
if (loopCount == 1) {
next = processLoop(r, loops.get(0), stack);
processed = true;
} else {
for (LoopInfo loop : loops) {
if (loop.getStart() == block) {
next = processLoop(r, loop, stack);
processed = true;
break;
}
}
}
}
if (!processed && block.getInstructions().size() == 1) {
InsnNode insn = block.getInstructions().get(0);
switch (insn.getType()) {
case IF:
next = processIf(r, block, (IfNode) insn, stack);
processed = true;
break;
case SWITCH:
next = processSwitch(r, block, (SwitchNode) insn, stack);
processed = true;
break;
case MONITOR_ENTER:
next = processMonitorEnter(r, block, insn, stack);
processed = true;
break;
default:
break;
}
}
if (!processed) {
r.getSubBlocks().add(block);
next = getNextBlock(block);
}
if (next != null && !stack.containsExit(block) && !stack.containsExit(next)) {
return next;
}
return null;
}
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<BlockNode>(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(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.setCondition(condInfo.getCondition());
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())) {
continue;
}
insertBreak(stack, 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.SKIP);
stack.addExit(loop.getEnd());
loopRegion.setBody(makeRegion(loopStart, stack));
loopStart.addAttr(AType.LOOP, loop);
loop.getEnd().remove(AFlag.SKIP);
} 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 = makeRegion(loopBody, stack);
// add blocks from loop start to first condition block
BlockNode conditionBlock = condInfo.getIfBlock();
if (loopStart != conditionBlock) {
Set<BlockNode> blocks = BlockUtils.getAllPathsBlocks(loopStart, conditionBlock);
blocks.remove(conditionBlock);
for (BlockNode block : blocks) {
if (block.getInstructions().isEmpty()
&& !block.contains(AFlag.SKIP)
&& !RegionUtils.isRegionContainsBlock(body, block)) {
body.add(block);
}
}
}
loopRegion.setBody(body);
}
stack.pop();
insertContinue(loop);
return out;
}
/**
* Select loop exit and construct LoopRegion
*/
private LoopRegion makeLoopRegion(IRegion curRegion, LoopInfo loop, List<BlockNode> exitBlocks) {
for (BlockNode block : exitBlocks) {
if (block.contains(AType.EXC_HANDLER)
|| block.getInstructions().size() != 1
|| block.getInstructions().get(0).getType() != InsnType.IF) {
continue;
}
List<LoopInfo> loops = block.getAll(AType.LOOP);
if (!loops.isEmpty() && loops.get(0) != loop) {
// skip nested loop condition
continue;
}
LoopRegion loopRegion = new LoopRegion(curRegion, loop, block, block == loop.getEnd());
boolean found;
if (block == loop.getStart() || block == loop.getEnd()
|| BlockUtils.isEmptySimplePath(loop.getStart(), block)) {
found = true;
} else if (block.getPredecessors().contains(loop.getStart())) {
loopRegion.setPreCondition(loop.getStart());
// if we can't merge pre-condition this is not correct header
found = loopRegion.checkPreCondition();
} else {
found = false;
}
if (found) {
List<LoopInfo> list = mth.getAllLoopsForBlock(block);
if (list.size() >= 2) {
// bad condition if successors going out of all loops
boolean allOuter = true;
for (BlockNode outerBlock : block.getCleanSuccessors()) {
List<LoopInfo> outLoopList = mth.getAllLoopsForBlock(outerBlock);
outLoopList.remove(loop);
if (!outLoopList.isEmpty()) {
// goes to outer loop
allOuter = false;
break;
}
}
if (allOuter) {
found = false;
}
}
}
if (found) {
return loopRegion;
}
}
// no exit found => endless loop
return null;
}
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);
stack.push(loopRegion);
BlockNode loopExit = null;
// insert 'break' for exits
List<Edge> exitEdges = loop.getExitEdges();
for (Edge exitEdge : exitEdges) {
BlockNode exit = exitEdge.getTarget();
if (insertBreak(stack, exit, exitEdge)) {
BlockNode nextBlock = getNextBlock(exit);
if (nextBlock != null) {
stack.addExit(nextBlock);
loopExit = nextBlock;
}
}
}
Region body = makeRegion(loopStart, stack);
BlockNode loopEnd = loop.getEnd();
if (!RegionUtils.isRegionContainsBlock(body, loopEnd)
&& !loopEnd.contains(AType.EXC_HANDLER)) {
body.getSubBlocks().add(loopEnd);
}
loopRegion.setBody(body);
if (loopExit == null) {
BlockNode next = getNextBlock(loopEnd);
loopExit = RegionUtils.isRegionContainsBlock(body, next) ? null : next;
}
stack.pop();
loopStart.addAttr(AType.LOOP, loop);
return loopExit;
}
private boolean canInsertBreak(BlockNode exit) {
if (exit.contains(AFlag.RETURN)
|| BlockUtils.checkLastInsnType(exit, InsnType.BREAK)) {
return false;
}
List<BlockNode> simplePath = BlockUtils.buildSimplePath(exit);
if (!simplePath.isEmpty()
&& simplePath.get(simplePath.size() - 1).contains(AFlag.RETURN)) {
return false;
}
// check if there no outer switch (TODO: very expensive check)
Set<BlockNode> paths = BlockUtils.getAllPathsBlocks(mth.getEnterBlock(), exit);
for (BlockNode block : paths) {
if (BlockUtils.checkLastInsnType(block, InsnType.SWITCH)) {
return false;
}
}
return true;
}
private boolean insertBreak(RegionStack stack, BlockNode loopExit, Edge exitEdge) {
BlockNode exit = exitEdge.getTarget();
BlockNode insertBlock = null;
boolean confirm = false;
// process special cases
if (loopExit == exit) {
// try/catch at loop end
BlockNode source = exitEdge.getSource();
if (source.contains(AType.CATCH_BLOCK)
&& source.getSuccessors().size() == 2) {
BlockNode other = BlockUtils.selectOther(loopExit, source.getSuccessors());
if (other != null) {
other = BlockUtils.skipSyntheticSuccessor(other);
if (other.contains(AType.EXC_HANDLER)) {
insertBlock = source;
confirm = true;
}
}
}
}
if (!confirm) {
while (exit != null) {
if (insertBlock != null && isPathExists(loopExit, exit)) {
// found cross
if (canInsertBreak(insertBlock)) {
confirm = true;
break;
}
return false;
}
insertBlock = exit;
List<BlockNode> cs = exit.getCleanSuccessors();
exit = cs.size() == 1 ? cs.get(0) : null;
}
}
if (!confirm) {
return false;
}
InsnNode breakInsn = new InsnNode(InsnType.BREAK, 0);
insertBlock.getInstructions().add(breakInsn);
stack.addExit(exit);
// add label to 'break' if needed
addBreakLabel(exitEdge, exit, breakInsn);
return true;
}
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);
}
}
private static void insertContinue(LoopInfo loop) {
BlockNode loopEnd = loop.getEnd();
List<BlockNode> predecessors = loopEnd.getPredecessors();
if (predecessors.size() <= 1) {
return;
}
Set<BlockNode> loopExitNodes = loop.getExitNodes();
for (BlockNode pred : predecessors) {
if (canInsertContinue(pred, predecessors, loopEnd, loopExitNodes)) {
InsnNode cont = new InsnNode(InsnType.CONTINUE, 0);
pred.getInstructions().add(cont);
}
}
}
private static boolean canInsertContinue(BlockNode pred, List<BlockNode> predecessors, BlockNode loopEnd,
Set<BlockNode> loopExitNodes) {
if (!pred.contains(AFlag.SYNTHETIC)
|| BlockUtils.checkLastInsnType(pred, InsnType.CONTINUE)) {
return false;
}
List<BlockNode> preds = pred.getPredecessors();
if (preds.isEmpty()) {
return false;
}
BlockNode codePred = preds.get(0);
if (codePred.contains(AFlag.SKIP)) {
return false;
}
if (loopEnd.isDominator(codePred)
|| loopExitNodes.contains(codePred)) {
return false;
}
if (isDominatedOnBlocks(codePred, predecessors)) {
return false;
}
boolean gotoExit = false;
for (BlockNode exit : loopExitNodes) {
if (BlockUtils.isPathExists(codePred, exit)) {
gotoExit = true;
break;
}
}
return gotoExit;
}
private static boolean isDominatedOnBlocks(BlockNode dom, List<BlockNode> blocks) {
for (BlockNode node : blocks) {
if (!node.isDominator(dom)) {
return false;
}
}
return true;
}
private final Set<BlockNode> cacheSet = new HashSet<BlockNode>();
private BlockNode processMonitorEnter(IRegion curRegion, BlockNode block, InsnNode insn, RegionStack stack) {
SynchronizedRegion synchRegion = new SynchronizedRegion(curRegion, insn);
synchRegion.getSubBlocks().add(block);
curRegion.getSubBlocks().add(synchRegion);
Set<BlockNode> exits = new HashSet<BlockNode>();
cacheSet.clear();
traverseMonitorExits(synchRegion, insn.getArg(0), block, exits, cacheSet);
for (InsnNode exitInsn : synchRegion.getExitInsns()) {
InstructionRemover.unbindInsn(mth, exitInsn);
}
BlockNode body = getNextBlock(block);
if (body == null) {
ErrorsCounter.methodError(mth, "Unexpected end of synchronized block");
return null;
}
BlockNode exit;
if (exits.size() == 1) {
exit = getNextBlock(exits.iterator().next());
} else {
cacheSet.clear();
exit = traverseMonitorExitsCross(body, exits, cacheSet);
}
stack.push(synchRegion);
stack.addExit(exit);
synchRegion.getSubBlocks().add(makeRegion(body, stack));
stack.pop();
return exit;
}
/**
* Traverse from monitor-enter thru successors and collect blocks contains monitor-exit
*/
private static void traverseMonitorExits(SynchronizedRegion region, InsnArg arg, BlockNode block,
Set<BlockNode> exits, Set<BlockNode> visited) {
visited.add(block);
for (InsnNode insn : block.getInstructions()) {
if (insn.getType() == InsnType.MONITOR_EXIT
&& insn.getArg(0).equals(arg)) {
exits.add(block);
region.getExitInsns().add(insn);
return;
}
}
for (BlockNode node : block.getCleanSuccessors()) {
if (!visited.contains(node)) {
traverseMonitorExits(region, arg, node, exits, visited);
}
}
}
/**
* Traverse from monitor-enter thru successors and search for exit paths cross
*/
private static BlockNode traverseMonitorExitsCross(BlockNode block, Set<BlockNode> exits, Set<BlockNode> visited) {
visited.add(block);
for (BlockNode node : block.getCleanSuccessors()) {
boolean cross = true;
for (BlockNode exitBlock : exits) {
boolean p = isPathExists(exitBlock, node);
if (!p) {
cross = false;
break;
}
}
if (cross) {
return node;
}
if (!visited.contains(node)) {
BlockNode res = traverseMonitorExitsCross(node, exits, visited);
if (res != null) {
return res;
}
}
}
return null;
}
private BlockNode processIf(IRegion currentRegion, BlockNode block, IfNode ifnode, RegionStack stack) {
if (block.contains(AFlag.SKIP)) {
// block already included in other 'if' region
return ifnode.getThenBlock();
}
IfInfo currentIf = makeIfInfo(block);
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(block);
currentIf = IfMakerHelper.restructureIf(mth, block, currentIf);
if (currentIf == null) {
// all attempts failed
return null;
}
}
confirmMerge(currentIf);
IfRegion ifRegion = new IfRegion(currentRegion, block);
ifRegion.setCondition(currentIf.getCondition());
currentRegion.getSubBlocks().add(ifRegion);
stack.push(ifRegion);
stack.addExit(currentIf.getOutBlock());
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));
}
stack.pop();
return currentIf.getOutBlock();
}
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());
assert c != null;
blocksMap.put(c, entry.getValue());
}
BitSet succ = BlockUtils.blocksToBitSet(mth, block.getSuccessors());
BitSet domsOn = BlockUtils.blocksToBitSet(mth, block.getDominatesOn());
domsOn.xor(succ); // filter 'out' block
BlockNode defCase = getBlockByOffset(insn.getDefaultCaseOffset(), block.getSuccessors());
if (defCase != null) {
blocksMap.remove(defCase);
}
int outCount = domsOn.cardinality();
if (outCount > 1) {
// remove exception handlers
BlockUtils.cleanBitSet(mth, domsOn);
outCount = domsOn.cardinality();
}
if (outCount > 1) {
// filter successors of other blocks
List<BlockNode> blocks = mth.getBasicBlocks();
for (int i = domsOn.nextSetBit(0); i >= 0; i = domsOn.nextSetBit(i + 1)) {
BlockNode b = blocks.get(i);
for (BlockNode s : b.getCleanSuccessors()) {
domsOn.clear(s.getId());
}
}
outCount = domsOn.cardinality();
}
BlockNode out = null;
if (outCount == 1) {
out = mth.getBasicBlocks().get(domsOn.nextSetBit(0));
} else if (outCount == 0) {
// one or several case blocks are empty,
// 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) {
out = maybeOut;
break;
}
}
}
stack.push(sw);
if (out != null) {
stack.addExit(out);
}
if (!stack.containsExit(defCase)) {
sw.setDefaultCase(makeRegion(defCase, stack));
}
for (Entry<BlockNode, List<Object>> entry : blocksMap.entrySet()) {
BlockNode c = entry.getKey();
if (stack.containsExit(c)) {
// empty case block
sw.addCase(entry.getValue(), new Region(stack.peekRegion()));
} else {
sw.addCase(entry.getValue(), makeRegion(c, stack));
}
}
stack.pop();
return out;
}
public void processTryCatchBlocks(MethodNode mth) {
Set<TryCatchBlock> tcs = new HashSet<TryCatchBlock>();
for (ExceptionHandler handler : mth.getExceptionHandlers()) {
tcs.add(handler.getTryBlock());
}
for (TryCatchBlock tc : tcs) {
List<BlockNode> blocks = new ArrayList<BlockNode>(tc.getHandlersCount());
Set<BlockNode> splitters = new HashSet<BlockNode>();
for (ExceptionHandler handler : tc.getHandlers()) {
BlockNode handlerBlock = handler.getHandlerBlock();
if (handlerBlock != null) {
blocks.add(handlerBlock);
splitters.addAll(handlerBlock.getPredecessors());
} else {
LOG.debug(ErrorsCounter.formatErrorMsg(mth, "No exception handler block: " + handler));
}
}
Set<BlockNode> exits = new HashSet<BlockNode>();
for (BlockNode splitter : splitters) {
for (BlockNode handler : blocks) {
List<BlockNode> s = splitter.getSuccessors();
if (s.isEmpty()) {
LOG.debug(ErrorsCounter.formatErrorMsg(mth, "No successors for splitter: " + splitter));
continue;
}
BlockNode ss = s.get(0);
BlockNode cross = BlockUtils.getPathCross(mth, ss, handler);
if (cross != null && cross != ss && cross != handler) {
exits.add(cross);
}
}
}
for (ExceptionHandler handler : tc.getHandlers()) {
processExcHandler(handler, exits);
}
}
}
private void processExcHandler(ExceptionHandler handler, Set<BlockNode> exits) {
BlockNode start = handler.getHandlerBlock();
if (start == null) {
return;
}
// TODO extract finally part which exists in all handlers from same try block
// TODO add blocks common for several handlers to some region
RegionStack stack = new RegionStack(mth);
stack.addExits(exits);
BlockNode exit = BlockUtils.traverseWhileDominates(start, start);
if (exit != null && RegionUtils.isRegionContainsBlock(mth.getRegion(), exit)) {
stack.addExit(exit);
}
handler.setHandlerRegion(makeRegion(start, stack));
ExcHandlerAttr excHandlerAttr = start.get(AType.EXC_HANDLER);
handler.getHandlerRegion().addAttr(excHandlerAttr);
}
static boolean isEqualPaths(BlockNode b1, BlockNode b2) {
if (b1 == b2) {
return true;
}
if (b1 == null || b2 == null) {
return false;
}
if (isReturnBlocks(b1, b2)) {
return true;
}
if (isSyntheticPath(b1, b2)) {
return true;
}
return false;
}
private static boolean isSyntheticPath(BlockNode b1, BlockNode b2) {
if (!b1.isSynthetic() || !b2.isSynthetic()) {
return false;
}
BlockNode n1 = getNextBlock(b1);
BlockNode n2 = getNextBlock(b2);
return isEqualPaths(n1, n2);
}
private static boolean isReturnBlocks(BlockNode b1, BlockNode b2) {
if (!b1.isReturnBlock() || !b2.isReturnBlock()) {
return false;
}
List<InsnNode> b1Insns = b1.getInstructions();
List<InsnNode> b2Insns = b2.getInstructions();
if (b1Insns.size() != 1 || b2Insns.size() != 1) {
return false;
}
InsnNode i1 = b1Insns.get(0);
InsnNode i2 = b2Insns.get(0);
if (i1.getArgsCount() == 0 || i2.getArgsCount() == 0) {
return false;
}
return i1.getArg(0).equals(i2.getArg(0));
}
}