KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * FindBugs - Find bugs in Java programs
3  * Copyright (C) 2004 Dave Brosius <dbrosius@users.sourceforge.net>
4  * Copyright (C) 2004 University of Maryland
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  */

20
21 package edu.umd.cs.findbugs.detect;
22
23
24 import edu.umd.cs.findbugs.*;
25
26 import org.apache.bcel.classfile.Method;
27
28 /**
29  * Find occurrences of using the String "+" or "+=" operators
30  * within a loop. This is much less efficient than creating
31  * a dedicated StringBuffer object outside the loop, and
32  * then appending to it.
33  *
34  * @author Dave Brosius
35  * @author William Pugh
36  */

37 public class StringConcatenation extends BytecodeScanningDetector implements StatelessDetector {
38     private static final boolean DEBUG
39             = SystemProperties.getBoolean("sbsc.debug");
40
41     static final int SEEN_NOTHING = 0;
42     static final int SEEN_NEW = 1;
43     static final int SEEN_APPEND1 = 2;
44     static final int SEEN_APPEND2 = 3;
45     static final int CONSTRUCTED_STRING_ON_STACK = 4;
46     static final int POSSIBLE_CASE = 5;
47
48     private BugReporter bugReporter;
49     private boolean reportedThisMethod;
50
51     private int registerOnStack = -1;
52     private int stringSource = -1;
53     private int createPC = -1;
54     private int state = SEEN_NOTHING;
55
56     public StringConcatenation(BugReporter bugReporter) {
57         this.bugReporter = bugReporter;
58     }
59
60
61
62     @Override JavaDoc
63          public void visit(Method obj) {
64         if (DEBUG)
65             System.out.println("------------------- Analyzing " + obj.getName() + " ----------------");
66         reset();
67         reportedThisMethod = false;
68         super.visit(obj);
69     }
70
71     private void reset() {
72         state = SEEN_NOTHING;
73         createPC = -1;
74         registerOnStack = -1;
75         stringSource = -1;
76
77         // For debugging: print what call to reset() is being invoked.
78
// This helps figure out why the detector is failing to
79
// recognize a particular idiom.
80
if (DEBUG) System.out.println("Reset from: " + new Throwable JavaDoc().getStackTrace()[1]);
81     }
82
83     private boolean storeIntoRegister(int seen, int reg) {
84         switch (seen) {
85         case ASTORE_0:
86             return reg == 0;
87         case ASTORE_1:
88             return reg == 1;
89         case ASTORE_2:
90             return reg == 2;
91         case ASTORE_3:
92             return reg == 3;
93         case ASTORE:
94             return reg == getRegisterOperand();
95         default:
96             return false;
97         }
98     }
99
100     @Override JavaDoc
101          public void sawOpcode(int seen) {
102         if (reportedThisMethod) return;
103         int oldState = state;
104         if (DEBUG) System.out.println("Opcode: " + OPCODE_NAMES[seen]);
105         switch (state) {
106         case SEEN_NOTHING:
107             if ((seen == NEW)
108                     &&
109                     getClassConstantOperand().startsWith("java/lang/StringBu")) {
110                 state = SEEN_NEW;
111                 createPC = getPC();
112             }
113             break;
114
115         case SEEN_NEW:
116             if (DEBUG && seen == INVOKEVIRTUAL) {
117                 System.out.println("Invoke virtual");
118                 System.out.println(" " + getNameConstantOperand());
119                 System.out.println(" " + getClassConstantOperand());
120                 System.out.println(" " + getSigConstantOperand());
121             }
122             if (seen == INVOKEVIRTUAL
123                     && "append".equals(getNameConstantOperand())
124                     && getClassConstantOperand().startsWith("java/lang/StringBu")) {
125                 if (DEBUG) System.out.println("Saw string being appended from register " + registerOnStack);
126                 if (getSigConstantOperand().startsWith("(Ljava/lang/String;)")
127                         && registerOnStack >= 0) {
128                     if (DEBUG)
129                         System.out.println("Saw string being appended, source = " + registerOnStack);
130                     state = SEEN_APPEND1;
131                     stringSource = registerOnStack;
132                 } else
133                     reset();
134             }
135             break;
136         case SEEN_APPEND1:
137             if (storeIntoRegister(seen, stringSource))
138                 reset();
139             else if (seen == INVOKEVIRTUAL
140                     && "append".equals(getNameConstantOperand())
141                     && getClassConstantOperand().startsWith("java/lang/StringBu")) {
142                 state = SEEN_APPEND2;
143             }
144             break;
145
146         case SEEN_APPEND2:
147             if (storeIntoRegister(seen, stringSource))
148                 reset();
149             else if (seen == INVOKEVIRTUAL
150                     && "toString".equals(getNameConstantOperand())
151                     && getClassConstantOperand().startsWith("java/lang/StringBu")) {
152                 state = CONSTRUCTED_STRING_ON_STACK;
153             }
154             break;
155
156         case CONSTRUCTED_STRING_ON_STACK:
157             if (storeIntoRegister(seen, stringSource))
158                 state = POSSIBLE_CASE;
159             else
160                 reset();
161             break;
162
163         case POSSIBLE_CASE:
164             if (seen == GOTO
165                     && (getPC() - getBranchTarget()) < 300
166                     && getBranchTarget() < createPC) {
167                 bugReporter.reportBug(new BugInstance(this, "SBSC_USE_STRINGBUFFER_CONCATENATION", NORMAL_PRIORITY)
168                         .addClassAndMethod(this)
169                         .addSourceLine(this, createPC));
170                 // System.out.println("SBSC spread: " + (getPC() - getBranchTarget()));
171
reset();
172                 reportedThisMethod = true;
173             } else if ((seen == NEW)
174                     &&
175                     getClassConstantOperand().startsWith("java/lang/StringBu")) {
176                 state = SEEN_NEW;
177                 createPC = getPC();
178             }
179             break;
180         }
181         registerOnStack = -1;
182         switch (seen) {
183         case ALOAD_0:
184             registerOnStack = 0;
185             break;
186         case ALOAD_1:
187             registerOnStack = 1;
188             break;
189         case ALOAD_2:
190             registerOnStack = 2;
191             break;
192         case ALOAD_3:
193             registerOnStack = 3;
194             break;
195         case ALOAD:
196             registerOnStack = getRegisterOperand();
197             break;
198         }
199         if (DEBUG && state != oldState)
200             System.out.println("At PC " + getPC()
201                     + " changing from state " + oldState
202                     + " to state " + state
203                     + ", regOnStack = " + registerOnStack);
204     }
205 }
206
207 // vim:ts=4
208
Popular Tags