in src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java [121:282]
private Record getFinallyInformation(StructClass cl, StructMethod mt, RootStatement root, CatchAllStatement fstat) {
Map<BasicBlock, Boolean> mapLast = new HashMap<>();
BasicBlockStatement firstBlockStatement = fstat.getHandler().getBasichead();
BasicBlock firstBasicBlock = firstBlockStatement.getBlock();
Instruction instrFirst = firstBasicBlock.getInstruction(0);
int firstCode = switch (instrFirst.opcode) {
case CodeConstants.opc_pop -> 1;
case CodeConstants.opc_astore -> 2;
default -> 0;
};
ExprProcessor proc = new ExprProcessor(methodDescriptor, varProcessor);
proc.processStatement(root, cl);
SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();
ssa.splitVariables(root, mt);
List<Exprent> expressions = firstBlockStatement.getExprents();
VarVersion pair = new VarVersion((VarExprent)((AssignmentExprent)expressions.get(firstCode == 2 ? 1 : 0)).getLeft());
FlattenStatementsHelper flattenHelper = new FlattenStatementsHelper();
DirectGraph dgraph = flattenHelper.buildDirectGraph(root);
LinkedList<DirectNode> stack = new LinkedList<>();
stack.add(dgraph.first);
Set<DirectNode> setVisited = new HashSet<>();
while (!stack.isEmpty()) {
DirectNode node = stack.removeFirst();
if (setVisited.contains(node)) {
continue;
}
setVisited.add(node);
BasicBlockStatement blockStatement = null;
if (node.block != null) {
blockStatement = node.block;
}
else if (node.predecessors.size() == 1) {
blockStatement = node.predecessors.get(0).block;
}
boolean isTrueExit = true;
if (firstCode != 1) {
isTrueExit = false;
for (int i = 0; i < node.exprents.size(); i++) {
Exprent exprent = node.exprents.get(i);
if (firstCode == 0) {
List<Exprent> lst = exprent.getAllExprents();
lst.add(exprent);
boolean found = false;
for (Exprent expr : lst) {
if (expr.type == Exprent.EXPRENT_VAR && new VarVersion((VarExprent)expr).equals(pair)) {
found = true;
break;
}
}
if (found) {
found = false;
if (exprent.type == Exprent.EXPRENT_EXIT) {
ExitExprent exit = (ExitExprent)exprent;
if (exit.getExitType() == ExitExprent.EXIT_THROW && exit.getValue().type == Exprent.EXPRENT_VAR) {
found = true;
}
}
if (!found) {
return null;
}
else {
isTrueExit = true;
}
}
}
else { // firstCode == 2
// searching for a load instruction
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent assignment = (AssignmentExprent)exprent;
if (assignment.getRight().type == Exprent.EXPRENT_VAR &&
new VarVersion((VarExprent)assignment.getRight()).equals(pair)) {
Exprent next = null;
if (i == node.exprents.size() - 1) {
if (node.successors.size() == 1) {
DirectNode nd = node.successors.get(0);
if (!nd.exprents.isEmpty()) {
next = nd.exprents.get(0);
}
}
}
else {
next = node.exprents.get(i + 1);
}
boolean found = false;
if (next != null && next.type == Exprent.EXPRENT_EXIT) {
ExitExprent exit = (ExitExprent)next;
if (exit.getExitType() == ExitExprent.EXIT_THROW && exit.getValue().type == Exprent.EXPRENT_VAR &&
assignment.getLeft().equals(exit.getValue())) {
found = true;
}
}
if (!found) {
return null;
}
else {
isTrueExit = true;
}
}
}
}
}
}
// find finally exits
if (blockStatement != null && blockStatement.getBlock() != null) {
Statement handler = fstat.getHandler();
for (StatEdge edge : blockStatement.getSuccessorEdges(EdgeType.DIRECT_ALL)) {
if (edge.getType() != EdgeType.REGULAR && handler.containsStatement(blockStatement)
&& !handler.containsStatement(edge.getDestination())) {
Boolean existingFlag = mapLast.get(blockStatement.getBlock());
// note: the dummy node is also processed!
if (existingFlag == null || !existingFlag) {
mapLast.put(blockStatement.getBlock(), isTrueExit);
break;
}
}
}
}
stack.addAll(node.successors);
}
// an empty `finally` block?
if (fstat.getHandler().type == StatementType.BASIC_BLOCK) {
boolean isFirstLast = mapLast.containsKey(firstBasicBlock);
InstructionSequence seq = firstBasicBlock.getSeq();
boolean isEmpty = switch (firstCode) {
case 0 -> isFirstLast && seq.length() == 1;
case 1 -> seq.length() == 1;
case 2 -> isFirstLast ? seq.length() == 3 : seq.length() == 1;
default -> false;
};
if (isEmpty) {
firstCode = 3;
}
}
return new Record(firstCode, mapLast);
}