private void setSubroutineEdges() {

    final Map<BasicBlock, BasicBlock> subroutines = new LinkedHashMap<>();

    for (BasicBlock block : blocks) {

      if (block.getSeq().getLastInstr().opcode == CodeConstants.opc_jsr) {

        LinkedList<BasicBlock> stack = new LinkedList<>();
        LinkedList<LinkedList<BasicBlock>> stackJsrStacks = new LinkedList<>();

        Set<BasicBlock> setVisited = new HashSet<>();

        stack.add(block);
        stackJsrStacks.add(new LinkedList<>());

        while (!stack.isEmpty()) {

          BasicBlock node = stack.removeFirst();
          LinkedList<BasicBlock> jsrstack = stackJsrStacks.removeFirst();

          setVisited.add(node);

          switch (node.getSeq().getLastInstr().opcode) {
            case CodeConstants.opc_jsr:
              jsrstack.add(node);
              break;
            case CodeConstants.opc_ret:
              BasicBlock enter = jsrstack.getLast();
              BasicBlock exit = blocks.getWithKey(enter.id + 1); // FIXME: find successor in a better way

              if (exit != null) {
                if (!node.isSuccessor(exit)) {
                  node.addSuccessor(exit);
                }
                jsrstack.removeLast();
                subroutines.put(enter, exit);
              }
              else {
                throw new RuntimeException("ERROR: last instruction jsr");
              }
          }

          if (!jsrstack.isEmpty()) {
            for (BasicBlock succ : node.getSuccs()) {
              if (!setVisited.contains(succ)) {
                stack.add(succ);
                stackJsrStacks.add(new LinkedList<>(jsrstack));
              }
            }
          }
        }
      }
    }

    this.subroutines = subroutines;
  }
@param mapInstrBlocks mapping of instruction -> block
src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java