docs: private void build(Subroutine subroutine) throws CFGBuilderException {
          // Prime the work list
          subroutine.addEdgeAndExplore(subroutine.getEntry(), subroutine.getStartInstruction(), START_EDGE);
  
          // Keep going until all basic blocks in the subroutine have been added
          while (subroutine.hasMoreWork()) {
              WorkListItem item = subroutine.nextItem();
  
              InstructionHandle handle = item.getStartInstruction();
              BasicBlock basicBlock = item.getBasicBlock();
  
              // Add exception handler block (ETB) for exception-throwing
              // instructions
              if (isPEI(handle)) {
                  if (DEBUG) {
                      System.out.println("ETB block " + basicBlock.getLabel() + " for " + handle);
                  }
                  handleExceptions(subroutine, handle, basicBlock);
                  BasicBlock body = subroutine.allocateBasicBlock();
                  subroutine.addEdge(basicBlock, body, FALL_THROUGH_EDGE);
                  basicBlock = body;
              }
  
              if (DEBUG) {
                  System.out.println("BODY block " + basicBlock.getLabel() + " for " + handle);
              }
  
              if (!basicBlock.isEmpty()) {
                  throw new IllegalStateException("Block isn't empty!");
              }
  
              // Add instructions until we get to the end of the block
              boolean endOfBasicBlock = false;
              do {
                  Instruction ins = handle.getInstruction();
  
                  // Add the instruction to the block
                  if (DEBUG) {
                      System.out.println("BB " + basicBlock.getLabel() + ": adding" + handle);
                  }
                  basicBlock.addInstruction(handle);
                  subroutine.addInstruction(handle);
  
                  short opcode = ins.getOpcode();
  
                  // TODO: should check instruction to ensure that in a JSR
                  // subroutine
                  // no assignments are made to the local containing the return
                  // address.
                  // if (ins instanceof ASTORE) ...
  
                  if (opcode == Const.JSR || opcode == Const.JSR_W) {
                      // Find JSR subroutine, add it to subroutine work list if
                      // we haven't built a CFG for it yet
                      JsrInstruction jsr = (JsrInstruction) ins;
                      InstructionHandle jsrTarget = jsr.getTarget();
                      Subroutine jsrSubroutine = jsrSubroutineMap.get(jsrTarget);
                      if (jsrSubroutine == null) {
                          jsrSubroutine = new Subroutine(jsrTarget);
                          jsrSubroutineMap.put(jsrTarget, jsrSubroutine);
                          subroutineWorkList.add(jsrSubroutine);
                      }
  
                      // This ends the basic block.
                      // Add a JSR_EDGE to the successor.
                      // It will be replaced later by the inlined JSR subroutine.
                      subroutine.addEdgeAndExplore(basicBlock, handle.getNext(), JSR_EDGE);
                      endOfBasicBlock = true;
                  } else if (opcode == Const.RET) {
                      // End of JSR subroutine
                      subroutine.addEdge(basicBlock, subroutine.getExit(), RET_EDGE);
                      endOfBasicBlock = true;
                  } else {
                      TargetEnumeratingVisitor visitor = new TargetEnumeratingVisitor(handle, cpg);
                      if (visitor.isEndOfBasicBlock()) {
                          endOfBasicBlock = true;
  
                          // Add control edges as appropriate
                          if (visitor.instructionIsThrow()) {
                              handleExceptions(subroutine, handle, basicBlock);
                          } else if (visitor.instructionIsExit()) {
                              subroutine.setExitBlock(basicBlock);
                          } else if (visitor.instructionIsReturn()) {
                              subroutine.setReturnBlock(basicBlock);
                          } else {
                              Iterator<Target> i = visitor.targetIterator();
                              while (i.hasNext()) {
                                  Target target = i.next();
                                  subroutine.addEdgeAndExplore(basicBlock, target.getTargetInstruction(), target.getEdgeType());
                              }
                          }
                      }
                  }
  
                  if (!endOfBasicBlock) {
                      InstructionHandle next = handle.getNext();
                      if (next == null) {
                          throw new CFGBuilderException("Control falls off end of method: " + handle);
                      }
  
                      // Is the next instruction a control merge or a PEI?
                      if (isMerge(next) || isPEI(next)) {
                          subroutine.addEdgeAndExplore(basicBlock, next, FALL_THROUGH_EDGE);
                          endOfBasicBlock = true;
                      } else {
                          // Basic block continues
                          handle = next;
                      }
                  }
  
              } while (!endOfBasicBlock);
          }
      }