1 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 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 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 if (DEBUG) System.out.println("Reset from: " + new Throwable ().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 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 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 | Popular Tags |