in jar-infer/jar-infer-lib/src/main/java/com/uber/nullaway/jarinfer/DefinitelyDerefedParamsDriver.java [230:333]
private void analyzeFile(String pkgName, String inPath, boolean includeNonPublicClasses)
throws IOException, ClassHierarchyException {
InputStream jarIS = null;
if (inPath.endsWith(".jar") || inPath.endsWith(".aar")) {
jarIS = getInputStream(inPath);
if (jarIS == null) {
return;
}
} else if (!new File(inPath).exists()) {
return;
}
AnalysisScope scope = AnalysisScopeReader.instance.makeBasePrimordialScope(null);
scope.setExclusions(
new FileOfClasses(
new ByteArrayInputStream(DEFAULT_EXCLUSIONS.getBytes(StandardCharsets.UTF_8))));
if (jarIS != null) {
scope.addInputStreamForJarToScope(ClassLoaderReference.Application, jarIS);
} else {
AnalysisScopeReader.instance.addClassPathToScope(
inPath, scope, ClassLoaderReference.Application);
}
AnalysisOptions options = new AnalysisOptions(scope, null);
AnalysisCache cache = new AnalysisCacheImpl();
IClassHierarchy cha = ClassHierarchyFactory.makeWithRoot(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;
}
// Skip non-public / ABI classes
if (!cls.isPublic() && !includeNonPublicClasses) {
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 (shouldCheckMethod(mtd)) {
Preconditions.checkNotNull(mtd, "method not found");
DefinitelyDerefedParams analysisDriver = null;
String sign = "";
try {
// Parameter analysis
boolean isStatic = mtd.isStatic();
if (mtd.getNumberOfParameters() > (isStatic ? 0 : 1)) {
// For inferring parameter nullability, our criteria is based on finding
// unchecked dereferences of that parameter. We perform a quick bytecode
// check and skip methods containing no dereferences (i.e. method calls
// or field accesses) at all, avoiding the expensive IR/CFG generation
// step for these methods.
// Note that this doesn't apply to inferring return value nullability.
if (bytecodeHasAnyDereferences(mtd)) {
analysisDriver = getAnalysisDriver(mtd, options, cache);
Set<Integer> result = analysisDriver.analyze();
if (!isStatic) {
// subtract 1 from each parameter index to account for 'this' parameter
result =
result.stream().map(i -> i - 1).collect(ImmutableSet.toImmutableSet());
}
sign = getSignature(mtd);
LOG(DEBUG, "DEBUG", "analyzed method: " + sign);
if (!result.isEmpty() || DEBUG) {
nonnullParams.put(sign, result);
LOG(
DEBUG,
"DEBUG",
"Inferred Nonnull param for method: " + sign + " = " + result.toString());
}
}
}
// Return value analysis
analyzeReturnValue(options, cache, mtd, analysisDriver, sign);
} catch (Exception e) {
LOG(
DEBUG,
"DEBUG",
"Exception while scanning bytecodes for " + mtd + " " + e.getMessage());
}
}
}
}
}
}
long endTime = System.currentTimeMillis();
LOG(
VERBOSE,
"Stats",
inPath
+ " >> time(ms): "
+ (endTime - analysisStartTime)
+ ", bytecode size: "
+ analyzedBytes
+ ", rate (ms/KB): "
+ (analyzedBytes > 0 ? (((endTime - analysisStartTime) * 1000) / analyzedBytes) : 0));
}