public static void duplicateMergedMatchedExceptionCatchBlocks()

in src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java [407:500]


  public static void duplicateMergedMatchedExceptionCatchBlocks(@NotNull ControlFlowGraph graph, @NotNull StructClass cl) {
    if (!cl.hasRecordPatternSupport()) {
      return;
    }
    Map<BasicBlock, Set<ExceptionRangeCFG>> mapRanges = new HashMap<>();
    for (ExceptionRangeCFG range : graph.getExceptions()) {
      mapRanges.computeIfAbsent(range.getHandler(), k -> new HashSet<>()).add(range);
    }

    for (Entry<BasicBlock, Set<ExceptionRangeCFG>> ent : mapRanges.entrySet()) {
      BasicBlock handler = ent.getKey();
      Set<ExceptionRangeCFG> ranges = ent.getValue();

      if (ranges.size() == 1) {
        continue;
      }
      if (handler.getLastInstruction().opcode != CodeConstants.opc_athrow) {
        continue;
      }
      InstructionSequence seq = handler.getSeq();
      boolean found = false;
      for (int i = 0; i < seq.length(); i++) {
        Instruction instr = seq.getInstr(i);
        if (instr.opcode == CodeConstants.opc_new && instr.operandsCount() >= 1) {
          if (found) {
            found = false;
            break;
          }
          PooledConstant constant = cl.getPool().getConstant(instr.operand(0));
          if (constant instanceof PrimitiveConstant primitiveConstant &&
              "java/lang/MatchException".equals(primitiveConstant.value)) {
            found = true;
          }
        }
      }
      if (!found) {
        continue;
      }
      Set<String> exceptions = ranges.stream()
        .map(t -> t.getExceptionTypes())
        .flatMap(t -> t != null ? t.stream() : Stream.empty())
        .collect(Collectors.toSet());
      List<BasicBlock> successors = handler.getSuccessors();
      if (successors != null && successors.size() == 1 && successors.get(0).getSuccessors().isEmpty() &&
          successors.get(0).getSuccessorExceptions().isEmpty() &&
          //exceptions contain only one type of exceptions, and it is not null, because null defines `finally` blocks
          exceptions.size() == 1 && !exceptions.contains(null)) {
        for (ExceptionRangeCFG range : ranges) {
          BasicBlock newHandler = handler.clone(++graph.last_id);
          graph.getBlocks().addWithKey(newHandler, newHandler.id);
          // only exception predecessors from this range considered
          List<BasicBlock> lstPredExceptions = new ArrayList<>(handler.getPredecessorExceptions());
          lstPredExceptions.retainAll(range.getProtectedRange());
          // replace predecessors
          for (BasicBlock pred : lstPredExceptions) {
            ExceptionRangeCFG previousEdge = graph.getExceptionRange(handler, pred);
            pred.replaceSuccessor(handler, newHandler);
            if (previousEdge != null) {
              previousEdge.setHandler(newHandler);
            }
          }
          // replace successors
          List<BasicBlock> scExceptions = new ArrayList<>(handler.getSuccessorExceptions());
          for (BasicBlock nextException : scExceptions) {
            ExceptionRangeCFG nextEdge = graph.getExceptionRange(nextException, handler);
            newHandler.addSuccessorException(nextException);
            nextEdge.getProtectedRange().add(newHandler);
          }
          //add fast exit
          newHandler.addSuccessor(successors.get(0));
          for (BasicBlock successorException : handler.getSuccessorExceptions()) {
            newHandler.addSuccessorException(successorException);
            ExceptionRangeCFG previousEdge = graph.getExceptionRange(successorException, handler);
            if (previousEdge != null) {
              ArrayList<BasicBlock> newRanges = new ArrayList<>();
              newRanges.add(newHandler);
              ExceptionRangeCFG subRange = new ExceptionRangeCFG(newRanges, successorException, previousEdge.getExceptionTypes());
              graph.getExceptions().add(subRange);
              successorException.addPredecessorException(newHandler);
            }
          }
          range.setHandler(newHandler);
        }

        for (BasicBlock successorException : handler.getSuccessorExceptions()) {
          successorException.removePredecessorException(handler);
          if (successorException.getPredecessorExceptions().isEmpty()) {
            graph.removeBlock(successorException);
          }
        }
        graph.removeBlock(handler);
      }
    }
  }