use of jadx.core.dex.trycatch.TryCatchBlockAttr in project jadx by skylot.
the class BlockExceptionHandler method connectExcHandlers.
/**
* Wrap try blocks with top/bottom splitter and connect them to handler block.
* Sometimes try block can be handler block itself and should be connected before wrapping.
* Use queue for postpone try blocks not ready for wrap.
*/
private static void connectExcHandlers(MethodNode mth, List<TryCatchBlockAttr> tryBlocks) {
if (tryBlocks.isEmpty()) {
return;
}
int limit = tryBlocks.size() * 3;
int count = 0;
Deque<TryCatchBlockAttr> queue = new ArrayDeque<>(tryBlocks);
while (!queue.isEmpty()) {
TryCatchBlockAttr tryBlock = queue.removeFirst();
boolean complete = wrapBlocksWithTryCatch(mth, tryBlock);
if (!complete) {
// return to queue at the end
queue.addLast(tryBlock);
}
if (count++ > limit) {
throw new JadxRuntimeException("Try blocks wrapping queue limit reached! Please report as an issue!");
}
}
}
use of jadx.core.dex.trycatch.TryCatchBlockAttr in project jadx by skylot.
the class BlockExceptionHandler method checkTryCatchRelation.
private static boolean checkTryCatchRelation(List<TryCatchBlockAttr> tryBlocks, TryCatchBlockAttr outerTryBlock, TryCatchBlockAttr innerTryBlock) {
if (outerTryBlock.getBlocks().equals(innerTryBlock.getBlocks())) {
// same try blocks -> merge handlers
List<ExceptionHandler> handlers = Utils.concatDistinct(outerTryBlock.getHandlers(), innerTryBlock.getHandlers());
tryBlocks.add(new TryCatchBlockAttr(tryBlocks.size(), handlers, outerTryBlock.getBlocks()));
tryBlocks.remove(outerTryBlock);
tryBlocks.remove(innerTryBlock);
return true;
}
Set<BlockNode> handlerBlocks = innerTryBlock.getHandlers().stream().flatMap(eh -> eh.getBlocks().stream()).collect(Collectors.toSet());
boolean catchInHandler = handlerBlocks.stream().anyMatch(isHandlersIntersects(outerTryBlock));
boolean catchInTry = innerTryBlock.getBlocks().stream().anyMatch(isHandlersIntersects(outerTryBlock));
boolean blocksOutsideHandler = outerTryBlock.getBlocks().stream().anyMatch(b -> !handlerBlocks.contains(b));
boolean makeInner = catchInHandler && (catchInTry || blocksOutsideHandler);
if (makeInner && innerTryBlock.isAllHandler()) {
// inner try block can't have catch-all handler
outerTryBlock.setBlocks(Utils.concatDistinct(outerTryBlock.getBlocks(), innerTryBlock.getBlocks()));
innerTryBlock.clear();
return false;
}
if (makeInner) {
// convert to inner
List<BlockNode> mergedBlocks = Utils.concatDistinct(outerTryBlock.getBlocks(), innerTryBlock.getBlocks());
innerTryBlock.getHandlers().removeAll(outerTryBlock.getHandlers());
innerTryBlock.setOuterTryBlock(outerTryBlock);
outerTryBlock.addInnerTryBlock(innerTryBlock);
outerTryBlock.setBlocks(mergedBlocks);
return false;
}
if (innerTryBlock.getHandlers().containsAll(outerTryBlock.getHandlers())) {
// merge
List<BlockNode> mergedBlocks = Utils.concatDistinct(outerTryBlock.getBlocks(), innerTryBlock.getBlocks());
List<ExceptionHandler> handlers = Utils.concatDistinct(outerTryBlock.getHandlers(), innerTryBlock.getHandlers());
tryBlocks.add(new TryCatchBlockAttr(tryBlocks.size(), handlers, mergedBlocks));
tryBlocks.remove(outerTryBlock);
tryBlocks.remove(innerTryBlock);
return true;
}
return false;
}
use of jadx.core.dex.trycatch.TryCatchBlockAttr in project jadx by skylot.
the class BlockExceptionHandler method prepareTryBlocks.
private static List<TryCatchBlockAttr> prepareTryBlocks(MethodNode mth) {
Map<ExceptionHandler, List<BlockNode>> blocksByHandler = new HashMap<>();
for (BlockNode block : mth.getBasicBlocks()) {
CatchAttr catchAttr = block.get(AType.EXC_CATCH);
if (catchAttr != null) {
for (ExceptionHandler eh : catchAttr.getHandlers()) {
blocksByHandler.computeIfAbsent(eh, c -> new ArrayList<>()).add(block);
}
}
}
if (Consts.DEBUG_EXC_HANDLERS) {
LOG.debug("Input exception handlers:");
blocksByHandler.forEach((eh, blocks) -> LOG.debug(" {}, throw blocks: {}, handler blocks: {}", eh, blocks, eh.getBlocks()));
}
if (blocksByHandler.isEmpty()) {
// no catch blocks -> remove all handlers
mth.getExceptionHandlers().forEach(eh -> removeExcHandler(mth, eh));
} else {
// remove handlers without blocks in catch attribute
blocksByHandler.forEach((eh, blocks) -> {
if (blocks.isEmpty()) {
removeExcHandler(mth, eh);
}
});
}
BlockSplitter.detachMarkedBlocks(mth);
mth.clearExceptionHandlers();
if (mth.isNoExceptionHandlers()) {
return Collections.emptyList();
}
blocksByHandler.forEach((eh, blocks) -> {
// remove catches from same handler
blocks.removeAll(eh.getBlocks());
});
List<TryCatchBlockAttr> tryBlocks = new ArrayList<>();
blocksByHandler.forEach((eh, blocks) -> {
List<ExceptionHandler> handlers = new ArrayList<>(1);
handlers.add(eh);
tryBlocks.add(new TryCatchBlockAttr(tryBlocks.size(), handlers, blocks));
});
if (tryBlocks.size() > 1) {
// merge or mark as outer/inner
while (true) {
boolean restart = combineTryCatchBlocks(tryBlocks);
if (!restart) {
break;
}
}
}
checkForMultiCatch(mth, tryBlocks);
clearTryBlocks(mth, tryBlocks);
sortHandlers(mth, tryBlocks);
if (Consts.DEBUG_EXC_HANDLERS) {
LOG.debug("Result try-catch blocks:");
tryBlocks.forEach(tryBlock -> LOG.debug(" {}", tryBlock));
}
return tryBlocks;
}
use of jadx.core.dex.trycatch.TryCatchBlockAttr in project jadx by skylot.
the class BlockExceptionHandler method checkForMultiCatch.
private static void checkForMultiCatch(MethodNode mth, List<TryCatchBlockAttr> tryBlocks) {
boolean merged = false;
for (TryCatchBlockAttr tryBlock : tryBlocks) {
if (mergeMultiCatch(mth, tryBlock)) {
merged = true;
}
}
if (merged) {
BlockSplitter.detachMarkedBlocks(mth);
mth.clearExceptionHandlers();
}
}
use of jadx.core.dex.trycatch.TryCatchBlockAttr in project jadx by skylot.
the class BlockExceptionHandler method wrapBlocksWithTryCatch.
private static boolean wrapBlocksWithTryCatch(MethodNode mth, TryCatchBlockAttr tryCatchBlock) {
List<BlockNode> blocks = tryCatchBlock.getBlocks();
BlockNode top = searchTopBlock(mth, blocks);
if (top.getPredecessors().isEmpty() && top != mth.getEnterBlock()) {
return false;
}
BlockNode bottom = searchBottomBlock(mth, blocks);
if (Consts.DEBUG_EXC_HANDLERS) {
LOG.debug("TryCatch #{} split: top {}, bottom: {}", tryCatchBlock.id(), top, bottom);
}
BlockNode topSplitterBlock = getTopSplitterBlock(mth, top);
topSplitterBlock.add(AFlag.EXC_TOP_SPLITTER);
topSplitterBlock.add(AFlag.SYNTHETIC);
int totalHandlerBlocks = tryCatchBlock.getHandlers().stream().mapToInt(eh -> eh.getBlocks().size()).sum();
BlockNode bottomSplitterBlock;
if (bottom == null || totalHandlerBlocks == 0) {
bottomSplitterBlock = null;
} else {
BlockNode existBottomSplitter = BlockUtils.getBlockWithFlag(bottom.getSuccessors(), AFlag.EXC_BOTTOM_SPLITTER);
bottomSplitterBlock = existBottomSplitter != null ? existBottomSplitter : BlockSplitter.startNewBlock(mth, -1);
bottomSplitterBlock.add(AFlag.EXC_BOTTOM_SPLITTER);
bottomSplitterBlock.add(AFlag.SYNTHETIC);
BlockSplitter.connect(bottom, bottomSplitterBlock);
}
if (Consts.DEBUG_EXC_HANDLERS) {
LOG.debug("TryCatch #{} result splitters: top {}, bottom: {}", tryCatchBlock.id(), topSplitterBlock, bottomSplitterBlock);
}
connectSplittersAndHandlers(tryCatchBlock, topSplitterBlock, bottomSplitterBlock);
for (BlockNode block : blocks) {
TryCatchBlockAttr currentTCBAttr = block.get(AType.TRY_BLOCK);
if (currentTCBAttr == null || currentTCBAttr.getInnerTryBlocks().contains(tryCatchBlock)) {
block.addAttr(tryCatchBlock);
}
}
tryCatchBlock.setTopSplitter(topSplitterBlock);
topSplitterBlock.updateCleanSuccessors();
if (bottomSplitterBlock != null) {
bottomSplitterBlock.updateCleanSuccessors();
}
return true;
}
Aggregations