PageRenderTime 65ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/findbugs-1.3.9/src/java/edu/umd/cs/findbugs/detect/FindNullDeref.java

#
Java | 1576 lines | 1212 code | 220 blank | 144 comment | 322 complexity | 95998026ecabf143540fb11b43000d4d MD5 | raw file
Possible License(s): Apache-2.0, Unlicense, GPL-2.0, LGPL-2.0, LGPL-2.1, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * FindBugs - Find bugs in Java programs
  3. * Copyright (C) 2003-2007 University of Maryland
  4. *
  5. * This library is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU Lesser General Public
  7. * License as published by the Free Software Foundation; either
  8. * version 2.1 of the License, or (at your option) any later version.
  9. *
  10. * This library is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * Lesser General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Lesser General Public
  16. * License along with this library; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. */
  19. package edu.umd.cs.findbugs.detect;
  20. import java.util.BitSet;
  21. import java.util.Collection;
  22. import java.util.Collections;
  23. import java.util.HashSet;
  24. import java.util.Iterator;
  25. import java.util.LinkedList;
  26. import java.util.List;
  27. import java.util.Set;
  28. import java.util.SortedSet;
  29. import java.util.TreeSet;
  30. import javax.annotation.CheckForNull;
  31. import org.apache.bcel.Constants;
  32. import org.apache.bcel.classfile.Code;
  33. import org.apache.bcel.classfile.CodeException;
  34. import org.apache.bcel.classfile.ConstantPool;
  35. import org.apache.bcel.classfile.JavaClass;
  36. import org.apache.bcel.classfile.LineNumberTable;
  37. import org.apache.bcel.classfile.Method;
  38. import org.apache.bcel.generic.ATHROW;
  39. import org.apache.bcel.generic.ConstantPoolGen;
  40. import org.apache.bcel.generic.IFNONNULL;
  41. import org.apache.bcel.generic.IFNULL;
  42. import org.apache.bcel.generic.Instruction;
  43. import org.apache.bcel.generic.InstructionHandle;
  44. import org.apache.bcel.generic.InstructionTargeter;
  45. import org.apache.bcel.generic.InvokeInstruction;
  46. import org.apache.bcel.generic.MethodGen;
  47. import org.apache.bcel.generic.PUTFIELD;
  48. import org.apache.bcel.generic.ReturnInstruction;
  49. import org.objectweb.asm.Type;
  50. import edu.umd.cs.findbugs.BugAccumulator;
  51. import edu.umd.cs.findbugs.BugAnnotation;
  52. import edu.umd.cs.findbugs.BugInstance;
  53. import edu.umd.cs.findbugs.BugReporter;
  54. import edu.umd.cs.findbugs.Detector;
  55. import edu.umd.cs.findbugs.FieldAnnotation;
  56. import edu.umd.cs.findbugs.FindBugsAnalysisFeatures;
  57. import edu.umd.cs.findbugs.LocalVariableAnnotation;
  58. import edu.umd.cs.findbugs.MethodAnnotation;
  59. import edu.umd.cs.findbugs.OpcodeStack;
  60. import edu.umd.cs.findbugs.SourceLineAnnotation;
  61. import edu.umd.cs.findbugs.SystemProperties;
  62. import edu.umd.cs.findbugs.UseAnnotationDatabase;
  63. import edu.umd.cs.findbugs.annotations.NonNull;
  64. import edu.umd.cs.findbugs.ba.AnalysisContext;
  65. import edu.umd.cs.findbugs.ba.BasicBlock;
  66. import edu.umd.cs.findbugs.ba.CFG;
  67. import edu.umd.cs.findbugs.ba.CFGBuilderException;
  68. import edu.umd.cs.findbugs.ba.ClassContext;
  69. import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
  70. import edu.umd.cs.findbugs.ba.DataflowValueChooser;
  71. import edu.umd.cs.findbugs.ba.Edge;
  72. import edu.umd.cs.findbugs.ba.Hierarchy;
  73. import edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase;
  74. import edu.umd.cs.findbugs.ba.JavaClassAndMethod;
  75. import edu.umd.cs.findbugs.ba.Location;
  76. import edu.umd.cs.findbugs.ba.MissingClassException;
  77. import edu.umd.cs.findbugs.ba.NullnessAnnotation;
  78. import edu.umd.cs.findbugs.ba.NullnessAnnotationDatabase;
  79. import edu.umd.cs.findbugs.ba.OpcodeStackScanner;
  80. import edu.umd.cs.findbugs.ba.SignatureConverter;
  81. import edu.umd.cs.findbugs.ba.SignatureParser;
  82. import edu.umd.cs.findbugs.ba.XFactory;
  83. import edu.umd.cs.findbugs.ba.XField;
  84. import edu.umd.cs.findbugs.ba.XMethod;
  85. import edu.umd.cs.findbugs.ba.XMethodParameter;
  86. import edu.umd.cs.findbugs.ba.interproc.PropertyDatabase;
  87. import edu.umd.cs.findbugs.ba.npe.IsNullValue;
  88. import edu.umd.cs.findbugs.ba.npe.IsNullValueDataflow;
  89. import edu.umd.cs.findbugs.ba.npe.IsNullValueFrame;
  90. import edu.umd.cs.findbugs.ba.npe.NullDerefAndRedundantComparisonCollector;
  91. import edu.umd.cs.findbugs.ba.npe.NullDerefAndRedundantComparisonFinder;
  92. import edu.umd.cs.findbugs.ba.npe.NullValueUnconditionalDeref;
  93. import edu.umd.cs.findbugs.ba.npe.ParameterNullnessProperty;
  94. import edu.umd.cs.findbugs.ba.npe.ParameterNullnessPropertyDatabase;
  95. import edu.umd.cs.findbugs.ba.npe.PointerUsageRequiringNonNullValue;
  96. import edu.umd.cs.findbugs.ba.npe.RedundantBranch;
  97. import edu.umd.cs.findbugs.ba.npe.ReturnPathType;
  98. import edu.umd.cs.findbugs.ba.npe.ReturnPathTypeDataflow;
  99. import edu.umd.cs.findbugs.ba.npe.UsagesRequiringNonNullValues;
  100. import edu.umd.cs.findbugs.ba.type.TypeDataflow;
  101. import edu.umd.cs.findbugs.ba.type.TypeFrame;
  102. import edu.umd.cs.findbugs.ba.vna.ValueNumber;
  103. import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
  104. import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
  105. import edu.umd.cs.findbugs.ba.vna.ValueNumberSourceInfo;
  106. import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
  107. import edu.umd.cs.findbugs.classfile.ClassDescriptor;
  108. import edu.umd.cs.findbugs.classfile.DescriptorFactory;
  109. import edu.umd.cs.findbugs.classfile.Global;
  110. import edu.umd.cs.findbugs.classfile.analysis.AnnotationValue;
  111. import edu.umd.cs.findbugs.log.Profiler;
  112. import edu.umd.cs.findbugs.props.GeneralWarningProperty;
  113. import edu.umd.cs.findbugs.props.WarningProperty;
  114. import edu.umd.cs.findbugs.props.WarningPropertySet;
  115. import edu.umd.cs.findbugs.props.WarningPropertyUtil;
  116. import edu.umd.cs.findbugs.visitclass.Util;
  117. /**
  118. * A Detector to find instructions where a NullPointerException might be raised.
  119. * We also look for useless reference comparisons involving null and non-null
  120. * values.
  121. *
  122. * @author David Hovemeyer
  123. * @author William Pugh
  124. * @see edu.umd.cs.findbugs.ba.npe.IsNullValueAnalysis
  125. */
  126. public class FindNullDeref implements Detector, UseAnnotationDatabase,
  127. NullDerefAndRedundantComparisonCollector {
  128. public static final boolean DEBUG = SystemProperties
  129. .getBoolean("fnd.debug");
  130. private static final boolean DEBUG_NULLARG = SystemProperties
  131. .getBoolean("fnd.debug.nullarg");
  132. private static final boolean DEBUG_NULLRETURN = SystemProperties
  133. .getBoolean("fnd.debug.nullreturn");
  134. private static final boolean MARK_DOOMED = SystemProperties
  135. .getBoolean("fnd.markdoomed", true);
  136. private static final boolean REPORT_SAFE_METHOD_TARGETS = true;
  137. private static final String METHOD = SystemProperties
  138. .getProperty("fnd.method");
  139. private static final String CLASS = SystemProperties
  140. .getProperty("fnd.class");
  141. // Fields
  142. private final BugReporter bugReporter;
  143. private final BugAccumulator bugAccumulator;
  144. // Cached database stuff
  145. private ParameterNullnessPropertyDatabase unconditionalDerefParamDatabase;
  146. private boolean checkedDatabases = false;
  147. // Transient state
  148. private ClassContext classContext;
  149. private Method method;
  150. private IsNullValueDataflow invDataflow;
  151. private ValueNumberDataflow vnaDataflow;
  152. private BitSet previouslyDeadBlocks;
  153. private NullnessAnnotation methodAnnotation;
  154. public FindNullDeref(BugReporter bugReporter) {
  155. this.bugReporter = bugReporter;
  156. this.bugAccumulator = new BugAccumulator(bugReporter);
  157. }
  158. public void visitClassContext(ClassContext classContext) {
  159. this.classContext = classContext;
  160. String currentMethod = null;
  161. JavaClass jclass = classContext.getJavaClass();
  162. String className = jclass.getClassName();
  163. if (CLASS != null && !className.equals(CLASS))
  164. return;
  165. for (Method method : classContext.getMethodsInCallOrder()) {
  166. try {
  167. if (method.isAbstract() || method.isNative()
  168. || method.getCode() == null)
  169. continue;
  170. currentMethod = SignatureConverter.convertMethodSignature(jclass, method);
  171. if (METHOD != null && !method.getName().equals(METHOD))
  172. continue;
  173. if (DEBUG || DEBUG_NULLARG)
  174. System.out.println("Checking for NP in " + currentMethod);
  175. analyzeMethod(classContext, method);
  176. } catch (MissingClassException e) {
  177. bugReporter.reportMissingClass(e.getClassNotFoundException());
  178. } catch (DataflowAnalysisException e) {
  179. bugReporter.logError("While analyzing " + currentMethod
  180. + ": FindNullDeref caught dae exception", e);
  181. } catch (CFGBuilderException e) {
  182. bugReporter.logError("While analyzing " + currentMethod
  183. + ": FindNullDeref caught cfgb exception", e);
  184. }
  185. bugAccumulator.reportAccumulatedBugs();
  186. }
  187. }
  188. private void analyzeMethod(ClassContext classContext, Method method) throws DataflowAnalysisException, CFGBuilderException
  189. {
  190. if (DEBUG || DEBUG_NULLARG)
  191. System.out.println("Pre FND ");
  192. if ((method.getAccessFlags() & Constants.ACC_VOLATILE) != 0)
  193. return;
  194. MethodGen methodGen = classContext.getMethodGen(method);
  195. if (methodGen == null)
  196. return;
  197. if (!checkedDatabases) {
  198. checkDatabases();
  199. checkedDatabases = true;
  200. }
  201. XMethod xMethod = XFactory.createXMethod(classContext.getJavaClass(), method);
  202. ClassDescriptor junitTestAnnotation = DescriptorFactory.createClassDescriptor("org/junit/Test");
  203. AnnotationValue av = xMethod.getAnnotation(junitTestAnnotation);
  204. if (av != null) {
  205. Object value = av.getValue("expected");
  206. if (value instanceof Type) {
  207. String className = ((Type)value).getClassName();
  208. if ( className.equals("java.lang.NullPointerException"))
  209. return;
  210. }
  211. }
  212. // UsagesRequiringNonNullValues uses =
  213. // classContext.getUsagesRequiringNonNullValues(method);
  214. this.method = method;
  215. this.methodAnnotation = getMethodNullnessAnnotation();
  216. if (DEBUG || DEBUG_NULLARG)
  217. System.out.println("FND: "
  218. + SignatureConverter.convertMethodSignature(methodGen));
  219. this.previouslyDeadBlocks = findPreviouslyDeadBlocks();
  220. // Get the IsNullValueDataflow for the method from the ClassContext
  221. invDataflow = classContext.getIsNullValueDataflow(method);
  222. vnaDataflow = classContext.getValueNumberDataflow(method);
  223. // Create a NullDerefAndRedundantComparisonFinder object to do the
  224. // actual
  225. // work. It will call back to report null derefs and redundant null
  226. // comparisons
  227. // through the NullDerefAndRedundantComparisonCollector interface we
  228. // implement.
  229. NullDerefAndRedundantComparisonFinder worker = new NullDerefAndRedundantComparisonFinder(
  230. classContext, method, this);
  231. worker.execute();
  232. checkCallSitesAndReturnInstructions();
  233. }
  234. /**
  235. * Find set of blocks which were known to be dead before doing the null
  236. * pointer analysis.
  237. *
  238. * @return set of previously dead blocks, indexed by block id
  239. * @throws CFGBuilderException
  240. * @throws DataflowAnalysisException
  241. */
  242. private BitSet findPreviouslyDeadBlocks() throws DataflowAnalysisException,
  243. CFGBuilderException {
  244. BitSet deadBlocks = new BitSet();
  245. ValueNumberDataflow vnaDataflow = classContext
  246. .getValueNumberDataflow(method);
  247. for (Iterator<BasicBlock> i = vnaDataflow.getCFG().blockIterator(); i
  248. .hasNext();) {
  249. BasicBlock block = i.next();
  250. ValueNumberFrame vnaFrame = vnaDataflow.getStartFact(block);
  251. if (vnaFrame.isTop()) {
  252. deadBlocks.set(block.getLabel());
  253. }
  254. }
  255. return deadBlocks;
  256. }
  257. /**
  258. * Check whether or not the various interprocedural databases we can use
  259. * exist and are nonempty.
  260. */
  261. private void checkDatabases() {
  262. AnalysisContext analysisContext = AnalysisContext
  263. .currentAnalysisContext();
  264. unconditionalDerefParamDatabase = analysisContext
  265. .getUnconditionalDerefParamDatabase();
  266. }
  267. private <DatabaseType extends PropertyDatabase<?, ?>> boolean isDatabaseNonEmpty(
  268. DatabaseType database) {
  269. return database != null && !database.isEmpty();
  270. }
  271. /**
  272. * See if the currently-visited method declares a
  273. *
  274. * @NonNull annotation, or overrides a method which declares a
  275. * @NonNull annotation.
  276. */
  277. private NullnessAnnotation getMethodNullnessAnnotation() {
  278. if (method.getSignature().indexOf(")L") >= 0
  279. || method.getSignature().indexOf(")[") >= 0) {
  280. if (DEBUG_NULLRETURN) {
  281. System.out.println("Checking return annotation for "
  282. + SignatureConverter.convertMethodSignature(
  283. classContext.getJavaClass(), method));
  284. }
  285. XMethod m = XFactory.createXMethod(classContext.getJavaClass(),
  286. method);
  287. return AnalysisContext.currentAnalysisContext()
  288. .getNullnessAnnotationDatabase().getResolvedAnnotation(m,
  289. false);
  290. }
  291. return NullnessAnnotation.UNKNOWN_NULLNESS;
  292. }
  293. static class CheckCallSitesAndReturnInstructions {}
  294. private void checkCallSitesAndReturnInstructions() {
  295. Profiler profiler = Global.getAnalysisCache().getProfiler();
  296. profiler.start(CheckCallSitesAndReturnInstructions.class);
  297. try {
  298. ConstantPoolGen cpg = classContext.getConstantPoolGen();
  299. TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
  300. for (Iterator<Location> i = classContext.getCFG(method)
  301. .locationIterator(); i.hasNext();) {
  302. Location location = i.next();
  303. Instruction ins = location.getHandle().getInstruction();
  304. try {
  305. ValueNumberFrame vnaFrame = classContext.getValueNumberDataflow(method).getFactAtLocation(location);
  306. if (!vnaFrame.isValid()) continue;
  307. if (ins instanceof InvokeInstruction) {
  308. examineCallSite(location, cpg, typeDataflow);
  309. } else if (methodAnnotation == NullnessAnnotation.NONNULL
  310. && ins.getOpcode() == Constants.ARETURN) {
  311. examineReturnInstruction(location);
  312. } else if (ins instanceof PUTFIELD) {
  313. examinePutfieldInstruction(location, (PUTFIELD) ins, cpg);
  314. }
  315. } catch (ClassNotFoundException e) {
  316. bugReporter.reportMissingClass(e);
  317. }
  318. }
  319. } catch (CheckedAnalysisException e) {
  320. AnalysisContext.logError("error:", e);
  321. } finally {
  322. profiler.end(CheckCallSitesAndReturnInstructions.class);
  323. }
  324. }
  325. private void examineCallSite(Location location, ConstantPoolGen cpg,
  326. TypeDataflow typeDataflow) throws DataflowAnalysisException,
  327. CFGBuilderException, ClassNotFoundException {
  328. InvokeInstruction invokeInstruction = (InvokeInstruction) location
  329. .getHandle().getInstruction();
  330. String methodName = invokeInstruction.getName(cpg);
  331. String signature = invokeInstruction.getSignature(cpg);
  332. // Don't check equals() calls.
  333. // If an equals() call unconditionally dereferences the parameter,
  334. // it is the fault of the method, not the caller.
  335. if (methodName.equals("equals")
  336. && signature.equals("(Ljava/lang/Object;)Z"))
  337. return;
  338. int returnTypeStart = signature.indexOf(')');
  339. if (returnTypeStart < 0)
  340. return;
  341. String paramList = signature.substring(0, returnTypeStart + 1);
  342. if (paramList.equals("()")
  343. || (paramList.indexOf("L") < 0 && paramList.indexOf('[') < 0))
  344. // Method takes no arguments, or takes no reference arguments
  345. return;
  346. // See if any null arguments are passed
  347. IsNullValueFrame frame = classContext.getIsNullValueDataflow(method)
  348. .getFactAtLocation(location);
  349. if (!frame.isValid())
  350. return;
  351. BitSet nullArgSet = frame.getArgumentSet(invokeInstruction, cpg,
  352. new DataflowValueChooser<IsNullValue>() {
  353. public boolean choose(IsNullValue value) {
  354. // Only choose non-exception values.
  355. // Values null on an exception path might be due to
  356. // infeasible control flow.
  357. return value.mightBeNull() && !value.isException()
  358. && !value.isReturnValue();
  359. }
  360. });
  361. BitSet definitelyNullArgSet = frame.getArgumentSet(invokeInstruction,
  362. cpg, new DataflowValueChooser<IsNullValue>() {
  363. public boolean choose(IsNullValue value) {
  364. return value.isDefinitelyNull();
  365. }
  366. });
  367. nullArgSet.and(definitelyNullArgSet);
  368. if (nullArgSet.isEmpty())
  369. return;
  370. if (DEBUG_NULLARG) {
  371. System.out.println("Null arguments passed: " + nullArgSet);
  372. System.out.println("Frame is: " + frame);
  373. System.out.println("# arguments: "
  374. + frame.getNumArguments(invokeInstruction, cpg));
  375. XMethod xm = XFactory.createXMethod(invokeInstruction, cpg);
  376. System.out.print("Signature: " + xm.getSignature());
  377. }
  378. if (unconditionalDerefParamDatabase != null) {
  379. checkUnconditionallyDereferencedParam(location, cpg, typeDataflow,
  380. invokeInstruction, nullArgSet, definitelyNullArgSet);
  381. }
  382. if (DEBUG_NULLARG) {
  383. System.out.println("Checking nonnull params");
  384. }
  385. checkNonNullParam(location, cpg, typeDataflow, invokeInstruction,
  386. nullArgSet, definitelyNullArgSet);
  387. }
  388. private void examinePutfieldInstruction(Location location, PUTFIELD ins,
  389. ConstantPoolGen cpg) throws DataflowAnalysisException,
  390. CFGBuilderException {
  391. IsNullValueDataflow invDataflow = classContext
  392. .getIsNullValueDataflow(method);
  393. IsNullValueFrame frame = invDataflow.getFactAtLocation(location);
  394. if (!frame.isValid())
  395. return;
  396. IsNullValue tos = frame.getTopValue();
  397. if (tos.isDefinitelyNull()) {
  398. XField field = XFactory.createXField(ins, cpg);
  399. NullnessAnnotation annotation = AnalysisContext
  400. .currentAnalysisContext().getNullnessAnnotationDatabase()
  401. .getResolvedAnnotation(field, false);
  402. if (annotation == NullnessAnnotation.NONNULL) {
  403. BugAnnotation variableAnnotation = null;
  404. try {
  405. ValueNumberFrame vnaFrame = classContext.getValueNumberDataflow(method).getFactAtLocation(location);
  406. ValueNumber valueNumber = vnaFrame.getTopValue();
  407. variableAnnotation = ValueNumberSourceInfo.findAnnotationFromValueNumber(method,
  408. location, valueNumber, vnaFrame, "VALUE_OF");
  409. } catch (DataflowAnalysisException e) {
  410. AnalysisContext.logError("error", e);
  411. } catch (CFGBuilderException e) {
  412. AnalysisContext.logError("error", e);
  413. }
  414. BugInstance warning = new BugInstance(this,
  415. "NP_STORE_INTO_NONNULL_FIELD",
  416. tos.isDefinitelyNull() ? HIGH_PRIORITY
  417. : NORMAL_PRIORITY).addClassAndMethod(classContext.getJavaClass(),method)
  418. .addField(field).addOptionalAnnotation(variableAnnotation).addSourceLine(classContext,
  419. method, location);
  420. bugReporter.reportBug(warning);
  421. }
  422. }
  423. }
  424. private void examineReturnInstruction(Location location)
  425. throws DataflowAnalysisException, CFGBuilderException {
  426. if (DEBUG_NULLRETURN) {
  427. System.out.println("Checking null return at " + location);
  428. }
  429. IsNullValueDataflow invDataflow = classContext
  430. .getIsNullValueDataflow(method);
  431. IsNullValueFrame frame = invDataflow.getFactAtLocation(location);
  432. ValueNumberFrame vnaFrame = classContext.getValueNumberDataflow(method).getFactAtLocation(location);
  433. if (!vnaFrame.isValid()) return;
  434. ValueNumber valueNumber = vnaFrame.getTopValue();
  435. if (!frame.isValid())
  436. return;
  437. IsNullValue tos = frame.getTopValue();
  438. if (tos.isDefinitelyNull()) {
  439. BugAnnotation variable = ValueNumberSourceInfo.findAnnotationFromValueNumber(method,
  440. location, valueNumber, vnaFrame, "VALUE_OF");
  441. String bugPattern = "NP_NONNULL_RETURN_VIOLATION";
  442. int priority = NORMAL_PRIORITY;
  443. if (tos.isDefinitelyNull() && !tos.isException())
  444. priority = HIGH_PRIORITY;
  445. String methodName = method.getName();
  446. if (methodName.equals("clone")) {
  447. bugPattern = "NP_CLONE_COULD_RETURN_NULL";
  448. priority = NORMAL_PRIORITY;
  449. } else if (methodName.equals("toString")) {
  450. bugPattern = "NP_TOSTRING_COULD_RETURN_NULL";
  451. priority = NORMAL_PRIORITY;
  452. }
  453. BugInstance warning = new BugInstance(this, bugPattern, priority)
  454. .addClassAndMethod(classContext.getJavaClass(), method).addOptionalAnnotation(variable);
  455. bugAccumulator.accumulateBug(warning, SourceLineAnnotation.fromVisitedInstruction(classContext, method,
  456. location));
  457. }
  458. }
  459. private boolean hasManyPreceedingNullTests(int pc) {
  460. int ifNullTests = 0;
  461. int ifNonnullTests = 0;
  462. BitSet seen = new BitSet();
  463. try {
  464. for (Iterator<Location> i = classContext.getCFG(method)
  465. .locationIterator(); i.hasNext();) {
  466. Location loc = i.next();
  467. int pc2 = loc.getHandle().getPosition();
  468. if (pc2 >= pc || pc2 < pc-30) continue;
  469. Instruction ins = loc.getHandle().getInstruction();
  470. if (ins instanceof IFNONNULL && !seen.get(pc2)) {
  471. ifNonnullTests++;
  472. seen.set(pc2);
  473. }
  474. else if (ins instanceof IFNULL && !seen.get(pc2)) {
  475. ifNullTests++;
  476. seen.set(pc2);
  477. }
  478. }
  479. boolean result = ifNullTests + ifNonnullTests > 2;
  480. // System.out.println("Preceeding null tests " + ifNullTests + " " + ifNonnullTests + " " + result);
  481. return result;
  482. } catch (CFGBuilderException e) {
  483. return false;
  484. }
  485. }
  486. private boolean safeCallToPrimateParseMethod(XMethod calledMethod, Location location) {
  487. if (calledMethod.getClassName().equals("java.lang.Integer")) {
  488. int position = location.getHandle().getPosition();
  489. ConstantPool constantPool = classContext.getJavaClass().getConstantPool();
  490. Code code = method.getCode();
  491. int catchSize = Util.getSizeOfSurroundingTryBlock(constantPool, code,
  492. "java/lang/NumberFormatException", position);
  493. if (catchSize < Integer.MAX_VALUE)
  494. return true;
  495. catchSize = Util.getSizeOfSurroundingTryBlock(constantPool, code,
  496. "java/lang/IllegalArgumentException", position);
  497. if (catchSize < Integer.MAX_VALUE)
  498. return true;
  499. catchSize = Util.getSizeOfSurroundingTryBlock(constantPool, code,
  500. "java/lang/RuntimeException", position);
  501. if (catchSize < Integer.MAX_VALUE)
  502. return true;
  503. }
  504. return false;
  505. }
  506. private void checkUnconditionallyDereferencedParam(Location location,
  507. ConstantPoolGen cpg, TypeDataflow typeDataflow,
  508. InvokeInstruction invokeInstruction, BitSet nullArgSet,
  509. BitSet definitelyNullArgSet) throws DataflowAnalysisException,
  510. ClassNotFoundException {
  511. boolean caught = inCatchNullBlock(location);
  512. if (caught && skipIfInsideCatchNull())
  513. return;
  514. // See what methods might be called here
  515. TypeFrame typeFrame = typeDataflow.getFactAtLocation(location);
  516. Set<JavaClassAndMethod> targetMethodSet = Hierarchy
  517. .resolveMethodCallTargets(invokeInstruction, typeFrame, cpg);
  518. if (DEBUG_NULLARG) {
  519. System.out.println("Possibly called methods: " + targetMethodSet);
  520. }
  521. // See if any call targets unconditionally dereference one of the null
  522. // arguments
  523. BitSet unconditionallyDereferencedNullArgSet = new BitSet();
  524. List<JavaClassAndMethod> dangerousCallTargetList = new LinkedList<JavaClassAndMethod>();
  525. List<JavaClassAndMethod> veryDangerousCallTargetList = new LinkedList<JavaClassAndMethod>();
  526. for (JavaClassAndMethod targetMethod : targetMethodSet) {
  527. if (DEBUG_NULLARG) {
  528. System.out.println("For target method " + targetMethod);
  529. }
  530. ParameterNullnessProperty property = unconditionalDerefParamDatabase
  531. .getProperty(targetMethod.toMethodDescriptor());
  532. if (property == null)
  533. continue;
  534. if (DEBUG_NULLARG) {
  535. System.out.println("\tUnconditionally dereferenced params: "
  536. + property);
  537. }
  538. BitSet targetUnconditionallyDereferencedNullArgSet = property
  539. .getViolatedParamSet(nullArgSet);
  540. if (targetUnconditionallyDereferencedNullArgSet.isEmpty())
  541. continue;
  542. dangerousCallTargetList.add(targetMethod);
  543. unconditionallyDereferencedNullArgSet
  544. .or(targetUnconditionallyDereferencedNullArgSet);
  545. if (!property.getViolatedParamSet(definitelyNullArgSet).isEmpty())
  546. veryDangerousCallTargetList.add(targetMethod);
  547. }
  548. if (dangerousCallTargetList.isEmpty())
  549. return;
  550. WarningPropertySet<WarningProperty> propertySet = new WarningPropertySet<WarningProperty>();
  551. // See if there are any safe targets
  552. Set<JavaClassAndMethod> safeCallTargetSet = new HashSet<JavaClassAndMethod>();
  553. safeCallTargetSet.addAll(targetMethodSet);
  554. safeCallTargetSet.removeAll(dangerousCallTargetList);
  555. if (safeCallTargetSet.isEmpty()) {
  556. propertySet
  557. .addProperty(NullArgumentWarningProperty.ALL_DANGEROUS_TARGETS);
  558. if (dangerousCallTargetList.size() == 1) {
  559. propertySet
  560. .addProperty(NullArgumentWarningProperty.MONOMORPHIC_CALL_SITE);
  561. }
  562. }
  563. // Call to private method? In theory there should be only one possible
  564. // target.
  565. boolean privateCall = safeCallTargetSet.isEmpty()
  566. && dangerousCallTargetList.size() == 1
  567. && dangerousCallTargetList.get(0).getMethod().isPrivate();
  568. String bugType;
  569. int priority;
  570. if (privateCall
  571. || invokeInstruction.getOpcode() == Constants.INVOKESTATIC
  572. || invokeInstruction.getOpcode() == Constants.INVOKESPECIAL) {
  573. bugType = "NP_NULL_PARAM_DEREF_NONVIRTUAL";
  574. priority = HIGH_PRIORITY;
  575. } else if (safeCallTargetSet.isEmpty()) {
  576. bugType = "NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS";
  577. priority = NORMAL_PRIORITY;
  578. } else {
  579. return;
  580. }
  581. if (caught)
  582. priority++;
  583. if (dangerousCallTargetList.size() > veryDangerousCallTargetList.size())
  584. priority++;
  585. else
  586. propertySet
  587. .addProperty(NullArgumentWarningProperty.ACTUAL_PARAMETER_GUARANTEED_NULL);
  588. XMethod calledFrom = XFactory.createXMethod(classContext.getJavaClass(), method);
  589. XMethod calledMethod = XFactory.createXMethod(invokeInstruction, cpg);
  590. if (safeCallToPrimateParseMethod(calledMethod, location)) return;
  591. BugInstance warning = new BugInstance(this,bugType, priority)
  592. .addClassAndMethod(classContext.getJavaClass(), method).addMethod(
  593. calledMethod)
  594. .describe(MethodAnnotation.METHOD_CALLED).addSourceLine(classContext,
  595. method, location);
  596. boolean uncallable = false;
  597. if (!AnalysisContext.currentXFactory().isCalledDirectlyOrIndirectly(calledFrom)
  598. && calledFrom.isPrivate()) {
  599. propertySet
  600. .addProperty(GeneralWarningProperty.IN_UNCALLABLE_METHOD);
  601. uncallable = true;
  602. }
  603. // Check which params might be null
  604. addParamAnnotations(location,
  605. definitelyNullArgSet, unconditionallyDereferencedNullArgSet, propertySet, warning);
  606. if (bugType.equals("NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS")) {
  607. // Add annotations for dangerous method call targets
  608. for (JavaClassAndMethod dangerousCallTarget : veryDangerousCallTargetList) {
  609. warning.addMethod(dangerousCallTarget).describe(
  610. MethodAnnotation.METHOD_DANGEROUS_TARGET_ACTUAL_GUARANTEED_NULL);
  611. }
  612. dangerousCallTargetList.removeAll(veryDangerousCallTargetList);
  613. if (DEBUG_NULLARG) {
  614. // Add annotations for dangerous method call targets
  615. for (JavaClassAndMethod dangerousCallTarget : dangerousCallTargetList) {
  616. warning.addMethod(dangerousCallTarget).describe(
  617. MethodAnnotation.METHOD_DANGEROUS_TARGET);
  618. }
  619. // Add safe method call targets.
  620. // This is useful to see which other call targets the analysis
  621. // considered.
  622. for (JavaClassAndMethod safeMethod : safeCallTargetSet) {
  623. warning.addMethod(safeMethod).describe(MethodAnnotation.METHOD_SAFE_TARGET);
  624. }
  625. }}
  626. decorateWarning(location, propertySet, warning);
  627. bugReporter.reportBug(warning);
  628. }
  629. private void decorateWarning(Location location,
  630. WarningPropertySet<WarningProperty> propertySet, BugInstance warning) {
  631. if (FindBugsAnalysisFeatures.isRelaxedMode()) {
  632. WarningPropertyUtil.addPropertiesForDataMining(propertySet,
  633. classContext, method, location);
  634. }
  635. propertySet.decorateBugInstance(warning);
  636. }
  637. private void addParamAnnotations(Location location,
  638. BitSet definitelyNullArgSet, BitSet violatedParamSet,
  639. WarningPropertySet<? super NullArgumentWarningProperty> propertySet, BugInstance warning) {
  640. ValueNumberFrame vnaFrame = null;
  641. try {
  642. vnaFrame = classContext.getValueNumberDataflow(method).getFactAtLocation(location);
  643. } catch (DataflowAnalysisException e) {
  644. AnalysisContext.logError("error", e);
  645. } catch (CFGBuilderException e) {
  646. AnalysisContext.logError("error", e);
  647. }
  648. InvokeInstruction instruction = (InvokeInstruction) location.getHandle().getInstruction();
  649. SignatureParser sigParser = new SignatureParser(instruction.getSignature(classContext.getConstantPoolGen()));
  650. for (int i = violatedParamSet.nextSetBit(0); i >= 0; i = violatedParamSet.nextSetBit(i + 1)) {
  651. boolean definitelyNull = definitelyNullArgSet.get(i);
  652. if (definitelyNull)
  653. propertySet
  654. .addProperty(NullArgumentWarningProperty.ARG_DEFINITELY_NULL);
  655. ValueNumber valueNumber = null;
  656. if (vnaFrame != null)
  657. try {
  658. valueNumber = vnaFrame. getArgument(instruction, classContext.getConstantPoolGen(), i, sigParser );
  659. BugAnnotation variableAnnotation = ValueNumberSourceInfo.findAnnotationFromValueNumber(method,
  660. location, valueNumber, vnaFrame, "VALUE_OF");
  661. warning.addOptionalAnnotation(variableAnnotation);
  662. } catch (DataflowAnalysisException e) {
  663. AnalysisContext.logError("error", e);
  664. }
  665. // Note: we report params as being indexed starting from 1, not
  666. // 0
  667. warning.addParameterAnnotation(i,
  668. definitelyNull ? "INT_NULL_ARG" : "INT_MAYBE_NULL_ARG");
  669. }
  670. }
  671. /**
  672. * We have a method invocation in which a possibly or definitely null
  673. * parameter is passed. Check it against the library of nonnull annotations.
  674. *
  675. * @param location
  676. * @param cpg
  677. * @param typeDataflow
  678. * @param invokeInstruction
  679. * @param nullArgSet
  680. * @param definitelyNullArgSet
  681. * @throws ClassNotFoundException
  682. */
  683. private void checkNonNullParam(Location location, ConstantPoolGen cpg,
  684. TypeDataflow typeDataflow, InvokeInstruction invokeInstruction,
  685. BitSet nullArgSet, BitSet definitelyNullArgSet)
  686. throws ClassNotFoundException {
  687. boolean caught = inCatchNullBlock(location);
  688. if (caught && skipIfInsideCatchNull())
  689. return;
  690. XMethod m = XFactory.createXMethod(invokeInstruction, cpg);
  691. INullnessAnnotationDatabase db = AnalysisContext
  692. .currentAnalysisContext().getNullnessAnnotationDatabase();
  693. SignatureParser sigParser = new SignatureParser(invokeInstruction.getSignature(cpg));
  694. for (int i = nullArgSet.nextSetBit(0); i >= 0; i = nullArgSet
  695. .nextSetBit(i + 1)) {
  696. if (db.parameterMustBeNonNull(m, i)) {
  697. boolean definitelyNull = definitelyNullArgSet.get(i);
  698. if (DEBUG_NULLARG) {
  699. System.out.println("Checking " + m);
  700. System.out.println("QQQ2: " + i + " -- " + i + " is null");
  701. System.out.println("QQQ nullArgSet: " + nullArgSet);
  702. System.out.println("QQQ dnullArgSet: "
  703. + definitelyNullArgSet);
  704. }
  705. BugAnnotation variableAnnotation = null;
  706. try {
  707. ValueNumberFrame vnaFrame = classContext.getValueNumberDataflow(method).getFactAtLocation(location);
  708. ValueNumber valueNumber = vnaFrame. getArgument(invokeInstruction, cpg, i, sigParser );
  709. variableAnnotation = ValueNumberSourceInfo.findAnnotationFromValueNumber(method,
  710. location, valueNumber, vnaFrame, "VALUE_OF");
  711. } catch (DataflowAnalysisException e) {
  712. AnalysisContext.logError("error", e);
  713. } catch (CFGBuilderException e) {
  714. AnalysisContext.logError("error", e);
  715. }
  716. int priority = definitelyNull ? HIGH_PRIORITY : NORMAL_PRIORITY;
  717. if (caught)
  718. priority++;
  719. String description = definitelyNull ? "INT_NULL_ARG" : "INT_MAYBE_NULL_ARG";
  720. BugInstance warning = new BugInstance(this,
  721. "NP_NONNULL_PARAM_VIOLATION", priority)
  722. .addClassAndMethod(classContext.getJavaClass(), method).addMethod(m)
  723. .describe(MethodAnnotation.METHOD_CALLED).addParameterAnnotation(i,
  724. description).addOptionalAnnotation(variableAnnotation).addSourceLine(
  725. classContext, method,
  726. location);
  727. bugReporter.reportBug(warning);
  728. }
  729. }
  730. }
  731. public void report() {
  732. }
  733. public boolean skipIfInsideCatchNull() {
  734. return classContext.getJavaClass().getClassName().indexOf("Test") >= 0
  735. || method.getName().indexOf("test") >= 0
  736. || method.getName().indexOf("Test") >= 0;
  737. }
  738. /**
  739. * @deprecated Use {@link #foundNullDeref(Location,ValueNumber,IsNullValue,ValueNumberFrame,boolean)} instead
  740. */
  741. public void foundNullDeref(Location location, ValueNumber valueNumber,
  742. IsNullValue refValue, ValueNumberFrame vnaFrame) {
  743. foundNullDeref(location, valueNumber, refValue, vnaFrame, true);
  744. }
  745. public void foundNullDeref(Location location, ValueNumber valueNumber,
  746. IsNullValue refValue, ValueNumberFrame vnaFrame, boolean isConsistent) {
  747. WarningPropertySet<WarningProperty> propertySet = new WarningPropertySet<WarningProperty>();
  748. if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT))
  749. return;
  750. boolean onExceptionPath = refValue.isException();
  751. if (onExceptionPath) {
  752. propertySet.addProperty(GeneralWarningProperty.ON_EXCEPTION_PATH);
  753. }
  754. int pc = location.getHandle().getPosition();
  755. BugAnnotation variable = ValueNumberSourceInfo.findAnnotationFromValueNumber(method,
  756. location, valueNumber, vnaFrame, "VALUE_OF");
  757. addPropertiesForDereferenceLocations(propertySet, Collections.singleton(location), isConsistent);
  758. Instruction ins = location.getHandle().getInstruction();
  759. if (ins instanceof InvokeInstruction && refValue.isDefinitelyNull()) {
  760. InvokeInstruction iins = (InvokeInstruction) ins;
  761. if (iins.getMethodName(classContext.getConstantPoolGen()).equals("close")
  762. && iins.getSignature(classContext.getConstantPoolGen()).equals("()V"))
  763. propertySet.addProperty(NullDerefProperty.CLOSING_NULL);
  764. }
  765. boolean duplicated = false;
  766. if (!isConsistent) {
  767. if (propertySet.containsProperty(NullDerefProperty.DEREFS_ARE_CLONED))
  768. duplicated = true;
  769. else try {
  770. CFG cfg = classContext.getCFG(method);
  771. if (cfg.getLocationsContainingInstructionWithOffset(pc)
  772. .size() > 1) {
  773. propertySet.addProperty(NullDerefProperty.DEREFS_ARE_INLINED_FINALLY_BLOCKS);
  774. duplicated = true;
  775. }
  776. } catch (CFGBuilderException e) {
  777. AnalysisContext.logError("huh", e);
  778. }
  779. }
  780. boolean caught = inCatchNullBlock(location);
  781. if (caught && skipIfInsideCatchNull())
  782. return;
  783. if (refValue.isDefinitelyNull()) {
  784. String type = "NP_ALWAYS_NULL";
  785. if (propertySet.containsProperty(NullDerefProperty.CLOSING_NULL))
  786. type = "NP_CLOSING_NULL";
  787. else if (onExceptionPath)
  788. type = "NP_ALWAYS_NULL_EXCEPTION";
  789. else if (duplicated)
  790. type = "NP_NULL_ON_SOME_PATH";
  791. int priority = onExceptionPath ? NORMAL_PRIORITY : HIGH_PRIORITY;
  792. if (caught)
  793. priority++;
  794. reportNullDeref(propertySet, location, type, priority, variable);
  795. } else if (refValue.mightBeNull() && refValue.isParamValue()) {
  796. String type;
  797. int priority = NORMAL_PRIORITY;
  798. if (caught)
  799. priority++;
  800. if (method.getName().equals("equals")
  801. && method.getSignature()
  802. .equals("(Ljava/lang/Object;)Z")) {
  803. if (caught)
  804. return;
  805. type = "NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT";
  806. } else
  807. type = "NP_ARGUMENT_MIGHT_BE_NULL";
  808. if (DEBUG)
  809. System.out.println("Reporting null on some path: value="
  810. + refValue);
  811. reportNullDeref(propertySet, location, type, priority, variable);
  812. }
  813. }
  814. private void reportNullDeref(WarningPropertySet<WarningProperty> propertySet,
  815. Location location, String type, int priority,
  816. @CheckForNull BugAnnotation variable) {
  817. BugInstance bugInstance = new BugInstance(this, type, priority)
  818. .addClassAndMethod(classContext.getJavaClass(), method);
  819. if (variable != null)
  820. bugInstance.add(variable);
  821. else
  822. bugInstance.add(new LocalVariableAnnotation("?", -1, -1));
  823. bugInstance.addSourceLine(classContext, method,
  824. location).describe("SOURCE_LINE_DEREF");
  825. if (FindBugsAnalysisFeatures.isRelaxedMode()) {
  826. WarningPropertyUtil.addPropertiesForDataMining(propertySet,
  827. classContext, method, location);
  828. }
  829. addPropertiesForDereferenceLocations(propertySet, Collections.singleton(location), false);
  830. propertySet.decorateBugInstance(bugInstance);
  831. bugReporter.reportBug(bugInstance);
  832. }
  833. public static boolean isThrower(BasicBlock target) {
  834. InstructionHandle ins = target.getFirstInstruction();
  835. int maxCount = 7;
  836. while (ins != null) {
  837. if (maxCount-- <= 0)
  838. break;
  839. Instruction i = ins.getInstruction();
  840. if (i instanceof ATHROW) {
  841. return true;
  842. }
  843. if (i instanceof InstructionTargeter
  844. || i instanceof ReturnInstruction)
  845. return false;
  846. ins = ins.getNext();
  847. }
  848. return false;
  849. }
  850. public void foundRedundantNullCheck(Location location,
  851. RedundantBranch redundantBranch) {
  852. boolean isChecked = redundantBranch.firstValue.isChecked();
  853. boolean wouldHaveBeenAKaboom = redundantBranch.firstValue
  854. .wouldHaveBeenAKaboom();
  855. Location locationOfKaBoom = redundantBranch.firstValue
  856. .getLocationOfKaBoom();
  857. boolean createdDeadCode = false;
  858. boolean infeasibleEdgeSimplyThrowsException = false;
  859. Edge infeasibleEdge = redundantBranch.infeasibleEdge;
  860. if (infeasibleEdge != null) {
  861. if (DEBUG)
  862. System.out.println("Check if " + redundantBranch
  863. + " creates dead code");
  864. BasicBlock target = infeasibleEdge.getTarget();
  865. if (DEBUG)
  866. System.out.println("Target block is "
  867. + (target.isExceptionThrower() ? " exception thrower"
  868. : " not exception thrower"));
  869. // If the block is empty, it probably doesn't matter that it was
  870. // killed.
  871. // FIXME: really, we should crawl the immediately reachable blocks
  872. // starting at the target block to see if any of them are dead and
  873. // nonempty.
  874. boolean empty = !target.isExceptionThrower()
  875. && (target.isEmpty() || isGoto(target.getFirstInstruction()
  876. .getInstruction()));
  877. if (!empty) {
  878. try {
  879. if (classContext.getCFG(method).getNumIncomingEdges(target) > 1) {
  880. if (DEBUG)
  881. System.out
  882. .println("Target of infeasible edge has multiple incoming edges");
  883. empty = true;
  884. }
  885. } catch (CFGBuilderException e) {
  886. assert true; // ignore it
  887. }
  888. }
  889. if (DEBUG)
  890. System.out.println("Target block is "
  891. + (empty ? "empty" : "not empty"));
  892. if (!empty) {
  893. if (isThrower(target))
  894. infeasibleEdgeSimplyThrowsException = true;
  895. }
  896. if (!empty && !previouslyDeadBlocks.get(target.getLabel())) {
  897. if (DEBUG)
  898. System.out.println("target was alive previously");
  899. // Block was not dead before the null pointer analysis.
  900. // See if it is dead now by inspecting the null value frame.
  901. // If it's TOP, then the block became dead.
  902. IsNullValueFrame invFrame = invDataflow.getStartFact(target);
  903. createdDeadCode = invFrame.isTop();
  904. if (DEBUG)
  905. System.out.println("target is now "
  906. + (createdDeadCode ? "dead" : "alive"));
  907. }
  908. }
  909. int priority;
  910. boolean valueIsNull = true;
  911. String warning;
  912. int pc = location.getHandle().getPosition();
  913. OpcodeStack stack = OpcodeStackScanner.getStackAt(classContext.getJavaClass(), method,pc);
  914. OpcodeStack.Item item1 = stack.getStackItem(0);
  915. OpcodeStack.Item item2 = null;
  916. if (redundantBranch.secondValue == null) {
  917. if (redundantBranch.firstValue.isDefinitelyNull()) {
  918. warning = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE";
  919. priority = NORMAL_PRIORITY;
  920. } else {
  921. warning = "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE";
  922. valueIsNull = false;
  923. priority = isChecked ? NORMAL_PRIORITY : LOW_PRIORITY;
  924. }
  925. } else {
  926. item2 = stack.getStackItem(1);
  927. boolean bothNull = redundantBranch.firstValue.isDefinitelyNull()
  928. && redundantBranch.secondValue.isDefinitelyNull();
  929. if (redundantBranch.secondValue.isChecked())
  930. isChecked = true;
  931. if (redundantBranch.secondValue.wouldHaveBeenAKaboom()) {
  932. wouldHaveBeenAKaboom = true;
  933. locationOfKaBoom = redundantBranch.secondValue
  934. .getLocationOfKaBoom();
  935. }
  936. if (bothNull) {
  937. warning = "RCN_REDUNDANT_COMPARISON_TWO_NULL_VALUES";
  938. priority = NORMAL_PRIORITY;
  939. } else {
  940. warning = "RCN_REDUNDANT_COMPARISON_OF_NULL_AND_NONNULL_VALUE";
  941. priority = isChecked ? NORMAL_PRIORITY : LOW_PRIORITY;
  942. }
  943. }
  944. if (wouldHaveBeenAKaboom) {
  945. priority = HIGH_PRIORITY;
  946. warning = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE";
  947. if (locationOfKaBoom == null)
  948. throw new NullPointerException("location of KaBoom is null");
  949. }
  950. if (DEBUG)
  951. System.out.println(createdDeadCode + " "
  952. + infeasibleEdgeSimplyThrowsException + " " + valueIsNull
  953. + " " + priority);
  954. if (createdDeadCode && !infeasibleEdgeSimplyThrowsException) {
  955. priority += 0;
  956. } else if (createdDeadCode && infeasibleEdgeSimplyThrowsException) {
  957. // throw clause
  958. if (valueIsNull)
  959. priority += 0;
  960. else
  961. priority += 1;
  962. } else {
  963. // didn't create any dead code
  964. priority += 1;
  965. }
  966. if (DEBUG) {
  967. System.out.println("RCN" + priority + " "
  968. + redundantBranch.firstValue + " =? "
  969. + redundantBranch.secondValue + " : " + warning);
  970. if (isChecked)
  971. System.out.println("isChecked");
  972. if (wouldHaveBeenAKaboom)
  973. System.out.println("wouldHaveBeenAKaboom");
  974. if (createdDeadCode)
  975. System.out.println("createdDeadCode");
  976. }
  977. if (priority > LOW_PRIORITY) return;
  978. BugAnnotation variableAnnotation = null;
  979. try {
  980. // Get the value number
  981. ValueNumberFrame vnaFrame = classContext.getValueNumberDataflow(
  982. method).getFactAtLocation(location);
  983. if (vnaFrame.isValid()) {
  984. Instruction ins = location.getHandle().getInstruction();
  985. ValueNumber valueNumber = vnaFrame.getInstance(ins,
  986. classContext.getConstantPoolGen());
  987. if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT))
  988. return;
  989. variableAnnotation = ValueNumberSourceInfo.findAnnotationFromValueNumber(method,
  990. location, valueNumber, vnaFrame, "VALUE_OF");
  991. }
  992. } catch (DataflowAnalysisException e) {
  993. // ignore
  994. } catch (CFGBuilderException e) {
  995. // ignore
  996. }
  997. BugInstance bugInstance = new BugInstance(this, warning, priority)
  998. .addClassAndMethod(classContext.getJavaClass(), method);
  999. LocalVariableAnnotation fallback = new LocalVariableAnnotation("?", -1, -1);
  1000. bugInstance.addOptionalUniqueAnnotationsWithFallback(fallback, variableAnnotation,
  1001. BugInstance.getFieldOrMethodValueSource(item1), BugInstance.getFieldOrMethodValueSource(item2));
  1002. if (wouldHaveBeenAKaboom)
  1003. bugInstance.addSourceLine(classContext, method,
  1004. locationOfKaBoom);
  1005. if (FindBugsAnalysisFeatures.isRelaxedMode()) {
  1006. WarningPropertySet<WarningProperty> propertySet = new WarningPropertySet<WarningProperty>();
  1007. WarningPropertyUtil.addPropertiesForDataMining(propertySet,
  1008. classContext, method, location);
  1009. if (isChecked)
  1010. propertySet.addProperty(NullDerefProperty.CHECKED_VALUE);
  1011. if (wouldHaveBeenAKaboom)
  1012. propertySet
  1013. .addProperty(NullDerefProperty.WOULD_HAVE_BEEN_A_KABOOM);
  1014. if (createdDeadCode)
  1015. propertySet.addProperty(NullDerefProperty.CREATED_DEAD_CODE);
  1016. propertySet.decorateBugInstance(bugInstance);
  1017. }
  1018. SourceLineAnnotation sourceLine = SourceLineAnnotation.fromVisitedInstruction(classContext, method,
  1019. location);
  1020. sourceLine.setDescription("SOURCE_REDUNDANT_NULL_CHECK");
  1021. bugAccumulator.accumulateBug(bugInstance, sourceLine);
  1022. }
  1023. BugAnnotation getVariableAnnotation(Location location) {
  1024. BugAnnotation variableAnnotation = null;
  1025. try {
  1026. // Get the value number
  1027. ValueNumberFrame vnaFrame = classContext.getValueNumberDataflow(
  1028. method).getFactAtLocation(location);
  1029. if (vnaFrame.isValid()) {
  1030. Instruction ins = location.getHandle().getInstruction();
  1031. ValueNumber valueNumber = vnaFrame.getInstance(ins,
  1032. classContext.getConstantPoolGen());
  1033. if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT))
  1034. return null;
  1035. variableAnnotation = ValueNumberSourceInfo.findAnnotationFromValueNumber(method,
  1036. location, valueNumber, vnaFrame, "VALUE_OF");
  1037. }
  1038. } catch (DataflowAnalysisException e) {
  1039. // ignore
  1040. } catch (CFGBuilderException e) {
  1041. // ignore
  1042. }
  1043. return variableAnnotation;
  1044. }
  1045. /**
  1046. * Determine whether or not given instruction is a goto.
  1047. *
  1048. * @param instruction
  1049. * the instruction
  1050. * @return true if the instruction is a goto, false otherwise
  1051. */
  1052. private boolean isGoto(Instruction instruction) {
  1053. return instruction.getOpcode() == Constants.GOTO
  1054. || instruction.getOpcode() == Constants.GOTO_W;
  1055. }
  1056. int minPC(Collection<Location> locs) {
  1057. int result = 1000000;
  1058. for(Location l : locs)
  1059. if (result > l.getHandle().getPosition())
  1060. result = l.getHandle().getPosition();
  1061. return result;
  1062. }
  1063. int maxPC(Collection<Location> locs) {
  1064. int result = -1000000;
  1065. for(Location l : locs)
  1066. if (result < l.getHandle().getPosition())
  1067. result = l.getHandle().getPosition();
  1068. return result;
  1069. }
  1070. /*
  1071. * (non-Javadoc)
  1072. *
  1073. * @see edu.umd.cs.findbugs.ba.npe.NullDerefAndRedundantComparisonCollector#foundGuaranteedNullDeref(java.util.Set,
  1074. * java.util.Set, edu.umd.cs.findbugs.ba.vna.ValueNumber, boolean)
  1075. */
  1076. public void foundGuaranteedNullDeref(
  1077. @NonNull Set<Location> assignedNullLocationSet,
  1078. @NonNull Set<Location> derefLocationSet,
  1079. SortedSet<Location> doomedLocations,
  1080. ValueNumberDataflow vna, ValueNumber refValue,
  1081. @CheckForNull BugAnnotation variableAnnotation, NullValueUnconditionalDeref deref,
  1082. boolean npeIfStatementCovered) {
  1083. if (refValue.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT))
  1084. return;
  1085. if (DEBUG) {
  1086. System.out.println("Found guaranteed null deref in "
  1087. + method.getName());
  1088. for (Location loc : doomedLocations)
  1089. System.out.println("Doomed at " + loc);
  1090. }
  1091. String bugType = "NP_GUARANTEED_DEREF";
  1092. int priority = NORMAL_PRIORITY;
  1093. if (deref.isMethodReturnValue())
  1094. bugType = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE";
  1095. else {
  1096. if (deref.isAlwaysOnExceptionPath())
  1097. bugType += "_ON_EXCEPTION_PATH";
  1098. else priority = HIGH_PRIORITY;
  1099. if (!npeIfStatementCovered)
  1100. priority++;
  1101. }
  1102. // Add Locations in the set of locations at least one of which
  1103. // is guaranteed to be dereferenced
  1104. SortedSet<Location> sourceLocations;
  1105. if (doomedLocations.isEmpty() || doomedLocations.size() > 3
  1106. && doomedLocations.size() > assignedNullLocationSet.size())
  1107. sourceLocations = new TreeSet<Location>(assignedNullLocationSet);
  1108. else
  1109. sourceLocations = doomedLocations;
  1110. if (doomedLocations.isEmpty() || derefLocationSet.isEmpty())
  1111. return;
  1112. WarningPropertySet<WarningProperty> propertySet = new WarningPropertySet<WarningProperty>();
  1113. addPropertiesForDereferenceLocations(propertySet, derefLocationSet, false);
  1114. int distance1 = minPC(derefLocationSet) - maxPC(assignedNullLocationSet);
  1115. int distance2 = minPC(derefLocationSet) - maxPC(doomedLocations);
  1116. int distance = Math.max(distance1, distance2);
  1117. if (false)
  1118. System.out.printf("%9d %9d %9d RANGE %s.%s%s\n",distance, distance1, distance2, classContext.getClassDescriptor().toDottedClassName(),
  1119. method.getName() ,method.getSignature());
  1120. // Create BugInstance
  1121. BitSet knownNull = new BitSet();
  1122. SortedSet<SourceLineAnnotation> knownNullLocations = new TreeSet<SourceLineAnnotation>();
  1123. for (Location loc : sourceLocations) {
  1124. SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation
  1125. .fromVisitedInstruction(classContext, method, loc);
  1126. if (sourceLineAnnotation == null)
  1127. continue;
  1128. int startLine = sourceLineAnnotation.getStartLine();
  1129. if (startLine == -1)
  1130. knownNullLocations.add(sourceLineAnnotation);
  1131. else if (!knownNull.get(startLine)) {
  1132. knownNull.set(startLine);
  1133. knownNullLocations.add(sourceLineAnnotation);
  1134. }
  1135. }
  1136. FieldAnnotation storedField = null;
  1137. MethodAnnotation invokedMethod = null;
  1138. XMethod invokedXMethod = null;
  1139. int parameterNumber = -1;
  1140. if (derefLocationSet.size() == 1) {
  1141. Location loc = derefLocationSet.iterator().next();
  1142. PointerUsageRequiringNonNullValue pu = null;
  1143. try {
  1144. UsagesRequiringNonNullValues usages = classContext.getUsagesRequiringNonNullValues(method);
  1145. pu = usages.get(loc, refValue, vnaDataflow);
  1146. } catch (DataflowAnalysisException e) {
  1147. AnalysisContext.logError("Error getting UsagesRequiringNonNullValues for " + method, e);
  1148. } catch (CFGBuilderException e) {
  1149. AnalysisContext.logError("Error getting UsagesRequiringNonNullValues for " + method, e);
  1150. }
  1151. if (pu != null) {
  1152. if (pu.getReturnFromNonNullMethod()) {
  1153. bugType = "NP_NONNULL_RETURN_VIOLATION";
  1154. String methodName = method.getName();
  1155. String methodSig = method.getSignature();
  1156. if (methodName.equals("clone") && methodSig.equals("()Ljava/lang/Object;")) {
  1157. bugType = "NP_CLONE_COULD_RETURN_NULL";
  1158. priority = NORMAL_PRIORITY;
  1159. } else if (methodName.equals("toString") && methodSig.equals("()Ljava/lang/String;")) {
  1160. bugType = "NP_TOSTRING_COULD_RETURN_NULL";
  1161. priority = NORMAL_PRIORITY;
  1162. }
  1163. } else {
  1164. XField nonNullField = pu.getNonNullField();
  1165. if (nonNullField != null) {
  1166. storedField = FieldAnnotation.fromXField( nonNullField );
  1167. bugType = "NP_STORE_INTO_NONNULL_FIELD";
  1168. } else {
  1169. XMethodParameter nonNullParameter = pu.getNonNullParameter();
  1170. if (nonNullParameter != null) {
  1171. XMethodParameter mp = nonNullParameter ;
  1172. invokedXMethod = mp.getMethod();
  1173. invokedMethod = MethodAnnotation.fromXMethod(mp.getMethod());
  1174. if (mp.getParameterNumber() == 0 && NullnessAnnotationDatabase.assertsFirstParameterIsNonnull(invokedXMethod))
  1175. return;
  1176. parameterNumber = mp.getParameterNumber();
  1177. bugType = "NP_NULL_PARAM_DEREF";
  1178. }
  1179. }
  1180. }
  1181. } else if (!deref.isAlwaysOnExceptionPath())
  1182. bugType = "NP_NULL_ON_SOME_PATH";
  1183. else
  1184. bugType = "NP_NULL_ON_SOME_PATH_EXCEPTION";
  1185. if (deref.isReadlineValue())
  1186. bugType = "NP_DEREFERENCE_OF_READLINE_VALUE";
  1187. else if (deref.isMethodReturnValue())
  1188. bugType = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE";
  1189. }
  1190. if (invokedXMethod != null) for (Location derefLoc : derefLocationSet)
  1191. if (safeCallToPrimateParseMethod(invokedXMethod, derefLoc)) return;
  1192. boolean hasManyNullTests = true;
  1193. for (SourceLineAnnotation sourceLineAnnotation : knownNullLocations) {
  1194. if (!hasManyPreceedingNullTests(sourceLineAnnotation.getStartBytecode()))
  1195. hasManyNullTests = false;
  1196. }
  1197. if (hasManyNullTests) {
  1198. if (bugType.equals("NP_NULL_ON_SOME_PATH"))
  1199. bugType = "NP_NULL_ON_SOME_PATH_MIGHT_BE_INFEASIBLE";
  1200. else priority++;
  1201. }
  1202. BugInstance bugInstance = new BugInstance(this, bugType, priority)
  1203. .addClassAndMethod(classContext.getJavaClass(), method);
  1204. if (invokedMethod != null)
  1205. bugInstance.addMethod(invokedMethod).describe(MethodAnnotation.METHOD_CALLED)
  1206. .addParameterAnnotation(parameterNumber, "INT_MAYBE_NULL_ARG");

Large files files are truncated, but you can click here to view the full file