docs: public static Result run(String inPaths, String pkgName, String outPath, boolean dbg, boolean vbs)
        throws IOException, ClassHierarchyException, IllegalArgumentException {
      DEBUG = dbg;
      VERBOSE = vbs;
      long start = System.currentTimeMillis();
      Set<String> setInPaths = new HashSet<>(Arrays.asList(inPaths.split(",")));
      for (String inPath : setInPaths) {
        InputStream jarIS = null;
        if (inPath.endsWith(".jar") || inPath.endsWith(".aar")) {
          jarIS = getInputStream(inPath);
          if (jarIS == null) {
            continue;
          }
        } else if (!new File(inPath).exists()) {
          continue;
        }
        AnalysisScope scope = AnalysisScopeReader.makePrimordialScope(null);
        scope.setExclusions(
            new FileOfClasses(
                new ByteArrayInputStream(DEFAULT_EXCLUSIONS.getBytes(StandardCharsets.UTF_8))));
        if (jarIS != null) scope.addInputStreamForJarToScope(ClassLoaderReference.Application, jarIS);
        else AnalysisScopeReader.addClassPathToScope(inPath, scope, ClassLoaderReference.Application);
        AnalysisOptions options = new AnalysisOptions(scope, null);
        AnalysisCache cache = new AnalysisCacheImpl();
        IClassHierarchy cha = ClassHierarchyFactory.makeWithPhantom(scope);
        Warnings.clear();
  
        // Iterate over all classes:methods in the 'Application' and 'Extension' class loaders
        for (IClassLoader cldr : cha.getLoaders()) {
          if (!cldr.getName().toString().equals("Primordial")) {
            for (IClass cls : Iterator2Iterable.make(cldr.iterateAllClasses())) {
              if (cls instanceof PhantomClass) continue;
              // Only process classes in specified classpath and not its dependencies.
              // TODO: figure the right way to do this
              if (!pkgName.isEmpty() && !cls.getName().toString().startsWith(pkgName)) continue;
              LOG(DEBUG, "DEBUG", "analyzing class: " + cls.getName().toString());
              for (IMethod mtd : Iterator2Iterable.make(cls.getDeclaredMethods().iterator())) {
                // Skip methods without parameters, abstract methods, native methods
                // some Application classes are Primordial (why?)
                if (!mtd.isPrivate()
                    && !mtd.isAbstract()
                    && !mtd.isNative()
                    && !isAllPrimitiveTypes(mtd)
                    && !mtd.getDeclaringClass()
                        .getClassLoader()
                        .getName()
                        .toString()
                        .equals("Primordial")) {
                  Preconditions.checkNotNull(mtd, "method not found");
                  DefinitelyDerefedParams analysisDriver = null;
                  String sign = "";
                  // Parameter analysis
                  if (mtd.getNumberOfParameters() > (mtd.isStatic() ? 0 : 1)) {
                    // Skip methods by looking at bytecode
                    try {
                      if (!CodeScanner.getFieldsRead(mtd).isEmpty()
                          || !CodeScanner.getFieldsWritten(mtd).isEmpty()
                          || !CodeScanner.getCallSites(mtd).isEmpty()) {
                        if (analysisDriver == null) {
                          analysisDriver = getAnalysisDriver(mtd, options, cache, cha);
                        }
                        Set<Integer> result = analysisDriver.analyze();
                        sign = getSignature(mtd);
                        LOG(DEBUG, "DEBUG", "analyzed method: " + sign);
                        if (!result.isEmpty() || DEBUG) {
                          map_result.put(sign, result);
                          LOG(
                              DEBUG,
                              "DEBUG",
                              "Inferred Nonnull param for method: "
                                  + sign
                                  + " = "
                                  + result.toString());
                        }
                      }
                    } catch (Exception e) {
                      LOG(
                          DEBUG,
                          "DEBUG",
                          "Exception while scanning bytecodes for " + mtd + " " + e.getMessage());
                    }
                  }
                  // Return value analysis
                  if (!mtd.getReturnType().isPrimitiveType()) {
                    if (analysisDriver == null) {
                      analysisDriver = getAnalysisDriver(mtd, options, cache, cha);
                    }
                    if (analysisDriver.analyzeReturnType()
                        == DefinitelyDerefedParams.NullnessHint.NULLABLE) {
                      if (sign.isEmpty()) {
                        sign = getSignature(mtd);
                      }
                      nullableReturns.add(sign);
                      LOG(DEBUG, "DEBUG", "Inferred Nullable method return: " + sign);
                    }
                  }
                }
              }
            }
          }
        }
        long end = System.currentTimeMillis();
        LOG(
            VERBOSE,
            "Stats",
            inPath
                + " >> time(ms): "
                + (end - start)
                + ", bytecode size: "
                + analyzedBytes
                + ", rate (ms/KB): "
                + (analyzedBytes > 0 ? (((end - start) * 1000) / analyzedBytes) : 0));
      }
      new File(outPath).getParentFile().mkdirs();
      if (outPath.endsWith(".astubx")) {
        writeModel(new DataOutputStream(new FileOutputStream(outPath)));
      } else {
        writeModelJAR(outPath);
      }
      lastOutPath = outPath;
      return map_result;
    }