docs: protected ReferenceType mergeReferenceTypes(ReferenceType aRef, ReferenceType bRef) throws DataflowAnalysisException {
          if (aRef.equals(bRef)) {
              return aRef;
          }
          byte aType = aRef.getType();
          byte bType = bRef.getType();
          try {
              // Special case: ExceptionObjectTypes.
              // We want to preserve the ExceptionSets associated,
              // in order to track the exact set of exceptions
              if (isObjectType(aType) && isObjectType(bType)
                      && ((aType == T_EXCEPTION || isThrowable(aRef))  && (bType == T_EXCEPTION ||   isThrowable(bRef)))) {
                  ExceptionSet union = exceptionSetFactory.createExceptionSet();
                  if (aType == Const.T_OBJECT && "Ljava/lang/Throwable;".equals(aRef.getSignature())) {
                      return aRef;
                  }
                  if (bType == Const.T_OBJECT && "Ljava/lang/Throwable;".equals(bRef.getSignature())) {
                      return bRef;
                  }
  
                  updateExceptionSet(union, (ObjectType) aRef);
                  updateExceptionSet(union, (ObjectType) bRef);
  
                  Type t = ExceptionObjectType.fromExceptionSet(union);
                  if (t instanceof ReferenceType) {
                      return (ReferenceType) t;
                  }
              }
  
              if (aRef instanceof GenericObjectType && bRef instanceof GenericObjectType
                      && aRef.getSignature().equals(bRef.getSignature())) {
                  GenericObjectType aG = (GenericObjectType) aRef;
                  GenericObjectType bG = (GenericObjectType) bRef;
                  if (aG.getTypeCategory() == bG.getTypeCategory()) {
                      switch (aG.getTypeCategory()) {
                      case PARAMETERIZED:
                          List<? extends ReferenceType> aP = aG.getParameters();
                          List<? extends ReferenceType> bP = bG.getParameters();
                          assert aP != null;
                          assert bP != null;
                          if (aP.size() != bP.size()) {
                              break;
                          }
                          ArrayList<ReferenceType> result = new ArrayList<>(aP.size());
                          for (int i = 0; i < aP.size(); i++) {
                              result.add(mergeReferenceTypes(aP.get(i), bP.get(i)));
                          }
  
                          GenericObjectType rOT = GenericUtilities.getType(aG.getClassName(), result);
                          return rOT;
  
                      }
  
                  }
  
              }
              if (aRef instanceof GenericObjectType) {
                  aRef = ((GenericObjectType) aRef).getObjectType();
              }
              if (bRef instanceof GenericObjectType) {
                  bRef = ((GenericObjectType) bRef).getObjectType();
              }
  
              if (Subtypes2.ENABLE_SUBTYPES2_FOR_COMMON_SUPERCLASS_QUERIES) {
                  return AnalysisContext.currentAnalysisContext().getSubtypes2().getFirstCommonSuperclass(aRef, bRef);
              } else {
                  return aRef.getFirstCommonSuperclass(bRef);
              }
          } catch (ClassNotFoundException e) {
              lookupFailureCallback.reportMissingClass(e);
              return Type.OBJECT;
          }
      }