/*
* FindBugs - Find bugs in Java programs
* Copyright (C) 2003-2005 University of Maryland
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.umd.cs.findbugs.detect;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.Code;
import edu.umd.cs.findbugs.BugAccumulator;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.BytecodeScanningDetector;
import edu.umd.cs.findbugs.SourceLineAnnotation;
import edu.umd.cs.findbugs.StatelessDetector;
import edu.umd.cs.findbugs.ba.ch.Subtypes2;
public class ReadReturnShouldBeChecked extends BytecodeScanningDetector implements StatelessDetector {
boolean sawRead = false;
boolean sawSkip = false;
boolean recentCallToAvailable = false;
int sawAvailable = 0;
boolean wasBufferedInputStream = false;
BugAccumulator accumulator;
private int locationOfCall;
private String lastCallClass = null, lastCallMethod = null, lastCallSig = null;
public ReadReturnShouldBeChecked(BugReporter bugReporter) {
this.accumulator = new BugAccumulator(bugReporter);
}
@Override
public void visit(Code obj) {
sawAvailable = 0;
sawRead = false;
sawSkip = false;
super.visit(obj);
accumulator.reportAccumulatedBugs();
}
private boolean isInputStream() {
if (lastCallClass.startsWith("["))
return false;
return (Subtypes2.instanceOf(lastCallClass, "java.io.InputStream")
|| Subtypes2.instanceOf(lastCallClass, "java.io.DataInput") || Subtypes2.instanceOf(lastCallClass,
"java.io.Reader")) && !Subtypes2.instanceOf(lastCallClass, "java.io.ByteArrayInputStream");
}
private boolean isBufferedInputStream() {
try {
if (lastCallClass.startsWith("["))
return false;
return Repository.instanceOf(lastCallClass, "java.io.BufferedInputStream");
} catch (ClassNotFoundException e) {
return false;
}
}
private boolean isImageIOInputStream() {
try {
if (lastCallClass.startsWith("["))
return false;
return Repository.instanceOf(lastCallClass, "javax.imageio.stream.ImageInputStream");
} catch (ClassNotFoundException e) {
return false;
}
}
@Override
public void sawOpcode(int seen) {
if (seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE) {
lastCallClass = getDottedClassConstantOperand();
lastCallMethod = getNameConstantOperand();
lastCallSig = getSigConstantOperand();
}
if (seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE)
if (getNameConstantOperand().equals("available") && getSigConstantOperand().equals("()I")
|| getNameConstantOperand().startsWith("get") && getNameConstantOperand().endsWith("Length")
&& getSigConstantOperand().equals("()I") || getClassConstantOperand().equals("java/io/File")
&& getNameConstantOperand().equals("length") && getSigConstantOperand().equals("()J")) {
sawAvailable = 70;
return;
}
sawAvailable--;
if ((seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE)
&& getNameConstantOperand().equals("read")
&& (getSigConstantOperand().equals("([B)I") || getSigConstantOperand().equals("([BII)I")
|| getSigConstantOperand().equals("([C)I") || getSigConstantOperand().equals("([CII)I"))
&& isInputStream()) {
sawRead = true;
recentCallToAvailable = sawAvailable > 0;
locationOfCall = getPC();
return;
}
if ((seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE)
&& (getNameConstantOperand().equals("skip") && getSigConstantOperand().equals("(J)J") || getNameConstantOperand()
.equals("skipBytes") && getSigConstantOperand().equals("(I)I")) && isInputStream()
&& !isImageIOInputStream()) {
// if not ByteArrayInput Stream
// and either no recent calls to length
// or it is a BufferedInputStream
wasBufferedInputStream = isBufferedInputStream();
sawSkip = true;
locationOfCall = getPC();
recentCallToAvailable = sawAvailable > 0 && !wasBufferedInputStream;
return;
}
if ((seen == POP) || (seen == POP2)) {
if (sawRead) {
accumulator.accumulateBug(
new BugInstance(this, "RR_NOT_CHECKED", recentCallToAvailable ? LOW_PRIORITY : NORMAL_PRIORITY)
.addClassAndMethod(this).addCalledMethod(lastCallClass, lastCallMethod, lastCallSig, false),
SourceLineAnnotation.fromVisitedInstruction(getClassContext(), this, locationOfCall));
} else if (sawSkip) {
accumulator.accumulateBug(
new BugInstance(this, "SR_NOT_CHECKED", (wasBufferedInputStream ? HIGH_PRIORITY
: recentCallToAvailable ? LOW_PRIORITY : NORMAL_PRIORITY)).addClassAndMethod(this)
.addCalledMethod(lastCallClass, lastCallMethod, lastCallSig, false), SourceLineAnnotation
.fromVisitedInstruction(getClassContext(), this, locationOfCall));
}
}
sawRead = false;
sawSkip = false;
}
}