private static CompletableFuture<Void> instanceOfObject(@Nullable Type subType,
                                                          @NotNull String superType,
                                                          CompletableFuture<Boolean> res) {
    if (subType == null || res.isDone()) {
      return completedFuture(null);
    }

    if (typeEquals(subType, superType)) {
      res.complete(true);
      return completedFuture(null); // early return
    }

    if (subType instanceof ClassType) {
      return allOf(
        superclass((ClassType)subType).thenCompose(s -> instanceOfObject(s, superType, res)),
        interfaces((ClassType)subType).thenCompose(
          interfaces -> allOf(interfaces.stream().map(i -> instanceOfObject(i, superType, res)).toArray(CompletableFuture[]::new))));
    }

    if (subType instanceof InterfaceType) {
      return allOf(
        superinterfaces((InterfaceType)subType).thenCompose(
          interfaces -> allOf(interfaces.stream().map(i -> instanceOfObject(i, superType, res)).toArray(CompletableFuture[]::new))));
    }

    if (subType instanceof ArrayType && superType.endsWith("[]")) {
      try {
        String superTypeItem = superType.substring(0, superType.length() - 2);
        Type subTypeItem = ((ArrayType)subType).componentType();
        return instanceOf(subTypeItem, superTypeItem, false).thenAccept(r -> {
          if (r) res.complete(true);
        });
      }
      catch (ClassNotLoadedException e) {
        //LOG.info(e);
      }
    }

    return completedFuture(null);
  }
private static CompletableFuture<Void> instanceOfObject(@Nullable Type subType,
                                                          @NotNull String superType,
                                                          CompletableFuture<Boolean> res)