001: /*
002: * FindBugs - Find bugs in Java programs
003: * Copyright (C) 2003,2004 University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs.detect;
021:
022: import java.util.HashMap;
023: import java.util.HashSet;
024: import java.util.Map;
025: import java.util.Set;
026: import java.util.TreeMap;
027: import java.util.TreeSet;
028:
029: import org.apache.bcel.classfile.Code;
030: import org.apache.bcel.classfile.JavaClass;
031:
032: import edu.umd.cs.findbugs.BugInstance;
033: import edu.umd.cs.findbugs.BugReporter;
034: import edu.umd.cs.findbugs.BytecodeScanningDetector;
035: import edu.umd.cs.findbugs.SystemProperties;
036:
037: public class InitializationChain extends BytecodeScanningDetector {
038: Set<String> requires = new TreeSet<String>();
039: Map<String, Set<String>> classRequires = new TreeMap<String, Set<String>>();
040: Set<String> staticFieldsAccessedInConstructor = new HashSet<String>();
041: Map<String, BugInstance> staticFieldWritten = new HashMap<String, BugInstance>();
042: private BugReporter bugReporter;
043: private boolean instanceCreated;
044: private int instanceCreatedPC;
045: private boolean instanceCreatedWarningGiven;
046:
047: private static final boolean DEBUG = SystemProperties
048: .getBoolean("ic.debug");
049:
050: public InitializationChain(BugReporter bugReporter) {
051: this .bugReporter = bugReporter;
052: }
053:
054: @Override
055: public void visit(Code obj) {
056: instanceCreated = false;
057: instanceCreatedWarningGiven = false;
058: if (!getMethodName().equals("<clinit>")
059: && !getMethodName().equals("<init>"))
060: return;
061: super .visit(obj);
062: requires.remove(getDottedClassName());
063: if (getDottedClassName().equals("java.lang.System")) {
064: requires.add("java.io.FileInputStream");
065: requires.add("java.io.FileOutputStream");
066: requires.add("java.io.BufferedInputStream");
067: requires.add("java.io.BufferedOutputStream");
068: requires.add("java.io.PrintStream");
069: }
070: if (!requires.isEmpty()) {
071: classRequires.put(getDottedClassName(), requires);
072: requires = new TreeSet<String>();
073: }
074: }
075:
076: @Override
077: public void visitAfter(JavaClass obj) {
078: for (String name : staticFieldsAccessedInConstructor) {
079: BugInstance bug = staticFieldWritten.get(name);
080: if (bug != null)
081: bugReporter.reportBug(bug);
082: }
083: staticFieldWritten.clear();
084: staticFieldsAccessedInConstructor.clear();
085:
086: }
087:
088: @Override
089: public void sawOpcode(int seen) {
090:
091: if (getMethodName().equals("<init>")) {
092: if (seen == GETSTATIC
093: && getClassConstantOperand().equals(getClassName())) {
094: staticFieldsAccessedInConstructor
095: .add(getNameConstantOperand());
096: }
097: return;
098: }
099:
100: if (seen == PUTSTATIC
101: && getClassConstantOperand().equals(getClassName())) {
102: // Don't do this check; it generates too many false
103: // positives. We need to do a more detailed check
104: // of which variables could be seen.
105: if (instanceCreated && !instanceCreatedWarningGiven
106: && !getSuperclassName().equals("java.lang.Enum")) {
107: String okSig = "L" + getClassName() + ";";
108: if (!okSig.equals(getSigConstantOperand())) {
109: staticFieldWritten
110: .put(
111: getNameConstantOperand(),
112: new BugInstance(
113: this ,
114: "SI_INSTANCE_BEFORE_FINALS_ASSIGNED",
115: NORMAL_PRIORITY)
116: .addClassAndMethod(this )
117: .addReferencedField(this )
118: .addSourceLine(this ,
119: instanceCreatedPC));
120: instanceCreatedWarningGiven = true;
121: }
122: }
123: } else if (seen == NEW
124: && getClassConstantOperand().equals(getClassName())) {
125: instanceCreated = true;
126: instanceCreatedPC = getPC();
127: } else if (seen == PUTSTATIC || seen == GETSTATIC
128: || seen == INVOKESTATIC || seen == NEW)
129: if (getPC() + 6 < codeBytes.length)
130: requires.add(getDottedClassConstantOperand());
131: }
132:
133: public void compute() {
134: Set<String> allClasses = classRequires.keySet();
135: Set<String> emptyClasses = new TreeSet<String>();
136: for (String c : allClasses) {
137: Set<String> needs = classRequires.get(c);
138: needs.retainAll(allClasses);
139: Set<String> extra = new TreeSet<String>();
140: for (String need : needs)
141: extra.addAll(classRequires.get(need));
142: needs.addAll(extra);
143: needs.retainAll(allClasses);
144: classRequires.put(c, needs);
145: if (needs.isEmpty())
146: emptyClasses.add(c);
147: }
148: for (String c : emptyClasses) {
149: classRequires.remove(c);
150: }
151: }
152:
153: @Override
154: public void report() {
155:
156: if (DEBUG)
157: System.out.println("Finishing computation");
158: compute();
159: compute();
160: compute();
161: compute();
162: compute();
163: compute();
164: compute();
165: compute();
166: Set<String> allClasses = classRequires.keySet();
167:
168: for (String c : allClasses) {
169: if (DEBUG)
170: System.out.println("Class " + c + " requires:");
171: for (String needs : (classRequires.get(c))) {
172: if (DEBUG)
173: System.out.println(" " + needs);
174: Set<String> s = classRequires.get(needs);
175: if (s != null && s.contains(c)
176: && c.compareTo(needs) < 0)
177: bugReporter.reportBug(new BugInstance(this ,
178: "IC_INIT_CIRCULARITY", NORMAL_PRIORITY)
179: .addClass(c).addClass(needs));
180: }
181: }
182: }
183:
184: }
|