edu.umd.cs.findbugs.detect.InitializationChain Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spotbugs Show documentation
Show all versions of spotbugs Show documentation
SpotBugs: Because it's easy!
/*
* FindBugs - Find bugs in Java programs
* Copyright (C) 2003,2004 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 java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.apache.bcel.Const;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.BytecodeScanningDetector;
import edu.umd.cs.findbugs.FieldAnnotation;
import edu.umd.cs.findbugs.MethodAnnotation;
import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.ba.XField;
import edu.umd.cs.findbugs.ba.XMethod;
public class InitializationChain extends BytecodeScanningDetector {
Set requires = new TreeSet<>();
Map> classRequires = new TreeMap<>();
private final BugReporter bugReporter;
private final Map> staticFieldsRead = new HashMap<>();
private final Set staticFieldsReadInAnyConstructor = new HashSet<>();
private Set fieldsReadInThisConstructor = new HashSet<>();
private final Set constructorsInvokedInStaticInitializer = new HashSet<>();
private final List invocationInfo = new ArrayList<>();
private final Set warningGiven = new HashSet<>();
private InvocationInfo lastInvocation;
static class InvocationInfo {
public InvocationInfo(XMethod constructor, int pc) {
this.constructor = constructor;
this.pc = pc;
}
XMethod constructor;
int pc;
XField field;
}
private static final boolean DEBUG = SystemProperties.getBoolean("ic.debug");
public InitializationChain(BugReporter bugReporter) {
this.bugReporter = bugReporter;
}
@Override
protected Iterable getMethodVisitOrder(JavaClass obj) {
ArrayList visitOrder = new ArrayList<>();
Method staticInitializer = null;
for (Method m : obj.getMethods()) {
String name = m.getName();
if (Const.STATIC_INITIALIZER_NAME.equals(name)) {
staticInitializer = m;
} else if (Const.CONSTRUCTOR_NAME.equals(name)) {
visitOrder.add(m);
}
}
if (staticInitializer != null) {
visitOrder.add(staticInitializer);
}
return visitOrder;
}
@Override
public void visit(Code obj) {
fieldsReadInThisConstructor = new HashSet<>();
super.visit(obj);
staticFieldsRead.put(getXMethod(), fieldsReadInThisConstructor);
requires.remove(getDottedClassName());
if ("java.lang.System".equals(getDottedClassName())) {
requires.add("java.io.FileInputStream");
requires.add("java.io.FileOutputStream");
requires.add("java.io.BufferedInputStream");
requires.add("java.io.BufferedOutputStream");
requires.add("java.io.PrintStream");
}
if (!requires.isEmpty()) {
classRequires.put(getDottedClassName(), requires);
requires = new TreeSet<>();
}
}
@Override
public void visitAfter(JavaClass obj) {
staticFieldsRead.clear();
staticFieldsReadInAnyConstructor.clear();
fieldsReadInThisConstructor.clear();
constructorsInvokedInStaticInitializer.clear();
invocationInfo.clear();
lastInvocation = null;
}
@Override
public void sawOpcode(int seen) {
InvocationInfo prev = lastInvocation;
lastInvocation = null;
if (Const.CONSTRUCTOR_NAME.equals(getMethodName())) {
if (seen == Const.GETSTATIC && getClassConstantOperand().equals(getClassName())) {
staticFieldsReadInAnyConstructor.add(getXFieldOperand());
fieldsReadInThisConstructor.add(getXFieldOperand());
}
return;
}
if (seen == Const.INVOKESPECIAL && Const.CONSTRUCTOR_NAME.equals(getNameConstantOperand()) && getClassConstantOperand().equals(
getClassName())) {
XMethod m = getXMethodOperand();
Set read = staticFieldsRead.get(m);
if (constructorsInvokedInStaticInitializer.add(m) && read != null && !read.isEmpty()) {
lastInvocation = new InvocationInfo(m, getPC());
invocationInfo.add(lastInvocation);
}
}
if (seen == Const.PUTSTATIC && getClassConstantOperand().equals(getClassName())) {
XField f = getXFieldOperand();
if (prev != null) {
prev.field = f;
}
if (staticFieldsReadInAnyConstructor.contains(f) && !warningGiven.contains(f)) {
for (InvocationInfo i : invocationInfo) {
Set fields = staticFieldsRead.get(i.constructor);
if (fields != null && fields.contains(f)) {
warningGiven.add(f);
BugInstance bug = new BugInstance(this, "SI_INSTANCE_BEFORE_FINALS_ASSIGNED", NORMAL_PRIORITY).addClassAndMethod(this);
if (i.field != null) {
bug.addField(i.field).describe(FieldAnnotation.STORED_ROLE);
}
bug.addMethod(i.constructor).describe(MethodAnnotation.METHOD_CONSTRUCTOR);
bug.addReferencedField(this).describe(FieldAnnotation.VALUE_OF_ROLE).addSourceLine(this, i.pc);
bugReporter.reportBug(bug);
break;
}
}
}
} else if (seen == Const.PUTSTATIC || seen == Const.GETSTATIC || seen == Const.INVOKESTATIC || seen == Const.NEW) {
if (getPC() + 6 < codeBytes.length) {
requires.add(getDottedClassConstantOperand());
}
}
}
public void compute() {
Set allClasses = classRequires.keySet();
Set emptyClasses = new TreeSet<>();
for (String c : allClasses) {
Set needs = classRequires.get(c);
needs.retainAll(allClasses);
Set extra = new TreeSet<>();
for (String need : needs) {
extra.addAll(classRequires.get(need));
}
needs.addAll(extra);
needs.retainAll(allClasses);
classRequires.put(c, needs);
if (needs.isEmpty()) {
emptyClasses.add(c);
}
}
for (String c : emptyClasses) {
classRequires.remove(c);
}
}
@Override
public void report() {
if (DEBUG) {
System.out.println("Finishing computation");
}
compute();
compute();
compute();
compute();
compute();
compute();
compute();
compute();
for (Entry> entry : classRequires.entrySet()) {
String c = entry.getKey();
if (DEBUG) {
System.out.println("Class " + c + " requires:");
}
for (String needs : entry.getValue()) {
if (DEBUG) {
System.out.println(" " + needs);
}
Set s = classRequires.get(needs);
if (s != null && s.contains(c) && c.compareTo(needs) < 0) {
bugReporter.reportBug(new BugInstance(this, "IC_INIT_CIRCULARITY", NORMAL_PRIORITY).addClass(c).addClass(
needs));
}
}
}
}
}