docs: private ExceptionSet computeThrownExceptionTypes(BasicBlock basicBlock) throws
      DataflowAnalysisException {
  
          ExceptionSet exceptionTypeSet = exceptionSetFactory.createExceptionSet();
          InstructionHandle pei = basicBlock.getExceptionThrower();
          Instruction ins = pei.getInstruction();
  
          // Get the exceptions that BCEL knows about.
          // Note that all of these are unchecked.
          ExceptionThrower exceptionThrower = (ExceptionThrower) ins;
          Class<?>[] exceptionList = exceptionThrower.getExceptions();
          for (Class<?> aExceptionList : exceptionList) {
              exceptionTypeSet.addImplicit(ObjectTypeFactory.getInstance(aExceptionList.getName()));
          }
  
          // Assume that an Error may be thrown by any instruction.
          exceptionTypeSet.addImplicit(Hierarchy.ERROR_TYPE);
  
          if (ins instanceof ATHROW) {
              // For ATHROW instructions, we generate *two* blocks
              // for which the ATHROW is an exception thrower.
              //
              // - The first, empty basic block, does the null check
              // - The second block, which actually contains the ATHROW,
              // throws the object on the top of the operand stack
              //
              // We make a special case of the block containing the ATHROW,
              // by removing all of the implicit exceptions,
              // and using type information to figure out what is thrown.
  
              if (basicBlock.containsInstruction(pei)) {
                  // This is the actual ATHROW, not the null check
                  // and implicit exceptions.
                  exceptionTypeSet.clear();
  
                  // The frame containing the thrown value is the start fact
                  // for the block, because ATHROW is guaranteed to be
                  // the only instruction in the block.
                  TypeFrame frame = getStartFact(basicBlock);
  
                  // Check whether or not the frame is valid.
                  // Sun's javac sometimes emits unreachable code.
                  // For example, it will emit code that follows a JSR
                  // subroutine call that never returns.
                  // If the frame is invalid, then we can just make
                  // a conservative assumption that anything could be
                  // thrown at this ATHROW.
                  if (!frame.isValid()) {
                      exceptionTypeSet.addExplicit(Type.THROWABLE);
                  } else if (frame.getStackDepth() == 0) {
                      throw new IllegalStateException("empty stack " + " thrown by " + pei + " in "
                              + SignatureConverter.convertMethodSignature(methodGen));
                  } else {
  
                      Type throwType = frame.getTopValue();
                      if (throwType instanceof ObjectType) {
                          exceptionTypeSet.addExplicit((ObjectType) throwType);
                      } else if (throwType instanceof ExceptionObjectType) {
                          exceptionTypeSet.addAll(((ExceptionObjectType) throwType).getExceptionSet());
                      } else {
                          // Not sure what is being thrown here.
                          // Be conservative.
                          if (DEBUG) {
                              System.out.println("Non object type " + throwType + " thrown by " + pei + " in "
                                      + SignatureConverter.convertMethodSignature(methodGen));
                          }
                          exceptionTypeSet.addExplicit(Type.THROWABLE);
                      }
                  }
              }
          }
  
          // If it's an InvokeInstruction, add declared exceptions and
          // RuntimeException
          if (ins instanceof InvokeInstruction) {
              ConstantPoolGen cpg = methodGen.getConstantPool();
  
              InvokeInstruction inv = (InvokeInstruction) ins;
              ObjectType[] declaredExceptionList = Hierarchy2.findDeclaredExceptions(inv, cpg);
              if (declaredExceptionList == null) {
                  // Couldn't find declared exceptions,
                  // so conservatively assume it could thrown any checked
                  // exception.
                  if (DEBUG) {
                      System.out.println("Couldn't find declared exceptions for "
                              + SignatureConverter.convertMethodSignature(inv, cpg));
                  }
                  exceptionTypeSet.addExplicit(Hierarchy.EXCEPTION_TYPE);
              } else {
                  for (ObjectType aDeclaredExceptionList : declaredExceptionList) {
                      exceptionTypeSet.addExplicit(aDeclaredExceptionList);
                  }
              }
  
              exceptionTypeSet.addImplicit(Hierarchy.RUNTIME_EXCEPTION_TYPE);
          }
  
          if (DEBUG) {
              System.out.println(pei + " can throw " + exceptionTypeSet);
          }
  
          return exceptionTypeSet;
      }