KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > detect > LazyInit


1 /*
2  * FindBugs - Find bugs in Java programs
3  * Copyright (C) 2004,2005 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
20 package edu.umd.cs.findbugs.detect;
21
22
23 import edu.umd.cs.findbugs.*;
24 import edu.umd.cs.findbugs.ba.*;
25 import edu.umd.cs.findbugs.ba.bcp.*;
26 import java.util.*;
27 import org.apache.bcel.Constants;
28 import org.apache.bcel.classfile.*;
29 import org.apache.bcel.generic.*;
30
31 /*
32  * Look for lazy initialization of fields which
33  * are not volatile. This is quite similar to checking for
34  * double checked locking, except that there is no lock.
35  *
36  * @author David Hovemeyer
37  */

38
39 public final class LazyInit extends ByteCodePatternDetector implements StatelessDetector {
40     private BugReporter bugReporter;
41
42     private static final boolean DEBUG = SystemProperties.getBoolean("lazyinit.debug");
43
44     /**
45      * The pattern to look for.
46      */

47     private static ByteCodePattern pattern = new ByteCodePattern();
48
49     static {
50         pattern
51                 .add(new Load("f", "val").label("start"))
52                 .add(new IfNull("val"))
53                 .add(new Wild(1, 1).label("createObject"))
54                 .add(new Store("f", pattern.dummyVariable()).label("end").dominatedBy("createObject"));
55     }
56
57     public LazyInit(BugReporter bugReporter) {
58         this.bugReporter = bugReporter;
59     }
60     @Override JavaDoc
61     public Object JavaDoc clone() {
62         try {
63             return super.clone();
64         } catch (CloneNotSupportedException JavaDoc e) {
65             throw new AssertionError JavaDoc(e);
66         }
67     }
68     @Override JavaDoc
69          public BugReporter getBugReporter() {
70         return bugReporter;
71     }
72
73
74
75     @Override JavaDoc
76          public ByteCodePattern getPattern() {
77         return pattern;
78     }
79
80     @Override JavaDoc
81          public boolean prescreen(Method method, ClassContext classContext) {
82         BitSet bytecodeSet = classContext.getBytecodeSet(method);
83         if (bytecodeSet == null) return false;
84         // The pattern requires a get/put pair accessing the same field.
85
if (!(bytecodeSet.get(Constants.GETSTATIC) && bytecodeSet.get(Constants.PUTSTATIC)) &&
86                 !(bytecodeSet.get(Constants.GETFIELD) && bytecodeSet.get(Constants.PUTFIELD)))
87             return false;
88
89         // If the method is synchronized, then we'll assume that
90
// things are properly synchronized
91
if (method.isSynchronized())
92             return false;
93
94         return true;
95     }
96
97     @Override JavaDoc
98          public void reportMatch(ClassContext classContext, Method method, ByteCodePatternMatch match)
99             throws CFGBuilderException, DataflowAnalysisException {
100         JavaClass javaClass = classContext.getJavaClass();
101         MethodGen methodGen = classContext.getMethodGen(method);
102         CFG cfg = classContext.getCFG(method);
103
104         try {
105             // Get the variable referenced in the pattern instance.
106
BindingSet bindingSet = match.getBindingSet();
107             Binding binding = bindingSet.lookup("f");
108
109             // Look up the field as an XField.
110
// If it is volatile, then the instance is not a bug.
111
FieldVariable field = (FieldVariable) binding.getVariable();
112             XField xfield =
113                     Hierarchy.findXField(field.getClassName(), field.getFieldName(), field.getFieldSig());
114             if (xfield == null || (xfield.getAccessFlags() & Constants.ACC_VOLATILE) != 0)
115                 return;
116
117             // XXX: for now, ignore lazy initialization of instance fields
118
if (!xfield.isStatic())
119                 return;
120
121             // Definitely ignore synthetic class$ fields
122
if (xfield.getName().startsWith("class$") || xfield.getName().startsWith("array$")) {
123                 if (DEBUG) System.out.println("Ignoring field " + xfield.getName());
124                 return;
125             }
126
127             // Ignore non-reference fields
128
if (!xfield.getSignature().startsWith("[")) {
129                 if (DEBUG) System.out.println("Ignoring non-reference field " + xfield.getName());
130                 return;
131             }
132
133             // TODO: Strings are safe to pass by data race in 1.5
134

135             // Get locations matching the beginning of the object creation,
136
// and the final field store.
137
PatternElementMatch createBegin = match.getFirstLabeledMatch("createObject");
138             PatternElementMatch store = match.getFirstLabeledMatch("end");
139
140             // Get all blocks
141
//
142
// (1) dominated by the wildcard instruction matching
143
// the beginning of the instructions creating the object, and
144
// (2) postdominated by the field store
145
//
146
// Exception edges are not considered in computing dominators/postdominators.
147
// We will consider this to be all of the code that creates
148
// the object.
149
DominatorsAnalysis domAnalysis =
150                     classContext.getNonExceptionDominatorsAnalysis(method);
151             PostDominatorsAnalysis postDomAnalysis =
152                     classContext.getNonExceptionPostDominatorsAnalysis(method);
153             BitSet extent = domAnalysis.getAllDominatedBy(createBegin.getBasicBlock());
154             extent.and(postDomAnalysis.getAllDominatedBy(store.getBasicBlock()));
155             //System.out.println("Extent: " + extent);
156
if (DEBUG) System.out.println("Object creation extent: " + extent);
157
158             // Check all instructions in the object creation extent
159
//
160
// (1) to determine the common lock set, and
161
// (2) to check for NEW and Invoke instructions that might create an object
162
//
163
// We ignore matches where a lock is held consistently,
164
// or if the extent does not appear to create a new object.
165
LockDataflow lockDataflow = classContext.getLockDataflow(method);
166             LockSet lockSet = null;
167             boolean sawNEW = false, sawINVOKE = false;
168             for (BasicBlock block : cfg.getBlocks(extent)) {
169                 for (Iterator<InstructionHandle> j = block.instructionIterator(); j.hasNext();) {
170                     InstructionHandle handle = j.next();
171
172                     Location location = new Location(handle, block);
173
174                     // Keep track of whether we saw any instructions
175
// that might actually have created a new object.
176
Instruction ins = handle.getInstruction();
177                     if (ins instanceof NEW)
178                         sawNEW = true;
179                     else if (ins instanceof InvokeInstruction)
180                         sawINVOKE = true;
181
182                     // Compute lock set intersection for all matched instructions.
183
LockSet insLockSet = lockDataflow.getFactAtLocation(location);
184                     if (lockSet == null) {
185                         lockSet = new LockSet();
186                         lockSet.copyFrom(insLockSet);
187                     } else
188                         lockSet.intersectWith(insLockSet);
189                 }
190             }
191             if (!(sawNEW || sawINVOKE))
192                 return;
193             if (lockSet == null) throw new IllegalStateException JavaDoc();
194             if (!lockSet.isEmpty())
195                 return;
196
197             // Compute the priority:
198
// - ignore lazy initialization of instance fields
199
// - when it's done in a public method, emit a high priority warning
200
// - protected or default access method, emit a medium priority warning
201
// - otherwise, low priority
202
int priority = LOW_PRIORITY;
203             boolean isDefaultAccess =
204                     (method.getAccessFlags() & (Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED)) == 0;
205             if (method.isPublic())
206                 priority = NORMAL_PRIORITY;
207             else if (method.isProtected() || isDefaultAccess)
208                 priority = NORMAL_PRIORITY;
209
210             // Report the bug.
211
InstructionHandle start = match.getLabeledInstruction("start");
212             InstructionHandle end = match.getLabeledInstruction("end");
213             String JavaDoc sourceFile = javaClass.getSourceFileName();
214             bugReporter.reportBug(new BugInstance(this, "LI_LAZY_INIT_STATIC", priority)
215                     .addClassAndMethod(methodGen, sourceFile)
216                     .addField(xfield).describe("FIELD_ON")
217                     .addSourceLine(classContext, methodGen, sourceFile, start, end));
218         } catch (ClassNotFoundException JavaDoc e) {
219             bugReporter.reportMissingClass(e);
220         }
221     }
222
223 }
224
225 // vim:ts=4
226
Popular Tags