1 20 package edu.umd.cs.findbugs.detect; 21 22 23 import edu.umd.cs.findbugs.*; 24 import edu.umd.cs.findbugs.ba.ClassContext; 25 import java.util.*; 26 import org.apache.bcel.*; 27 import org.apache.bcel.classfile.*; 28 import org.apache.bcel.generic.Type; 29 30 public class UselessSubclassMethod extends BytecodeScanningDetector implements StatelessDetector { 31 32 public static final int SEEN_NOTHING = 0; 33 public static final int SEEN_PARM = 1; 34 public static final int SEEN_LAST_PARM = 2; 35 public static final int SEEN_INVOKE = 3; 36 public static final int SEEN_RETURN = 4; 37 public static final int SEEN_INVALID = 5; 38 39 private BugReporter bugReporter; 40 private String superclassName; 41 private int state; 42 private int curParm; 43 private int curParmOffset; 44 private int invokePC; 45 private Type[] argTypes; 46 private Set<String > interfaceMethods = null; 47 48 public UselessSubclassMethod(BugReporter bugReporter) { 49 this.bugReporter = bugReporter; 50 } 51 52 53 54 @Override 55 public void visitClassContext(ClassContext classContext) { 56 try { 57 JavaClass cls = classContext.getJavaClass(); 58 superclassName = cls.getSuperclassName(); 59 JavaClass[] interfaces = null; 60 if (cls.isClass() && ((cls.getAccessFlags() & Constants.ACC_ABSTRACT) != 0)) { 61 interfaces = cls.getAllInterfaces(); 62 interfaceMethods = new HashSet<String >(); 63 for (JavaClass aInterface : interfaces) { 64 Method[] infMethods = aInterface.getMethods(); 65 for (Method meth : infMethods) { 66 interfaceMethods.add(meth.getName() + meth.getSignature()); 67 } 68 } 69 } 70 } catch (ClassNotFoundException cnfe) { 71 bugReporter.reportMissingClass(cnfe); 72 } 73 super.visitClassContext(classContext); 74 } 75 76 @Override 77 public void visitAfter(JavaClass obj) { 78 interfaceMethods = null; 79 super.visitAfter(obj); 80 } 81 82 @Override 83 public void visitMethod(Method obj) { 84 if ((interfaceMethods != null) && ((obj.getAccessFlags() & Constants.ACC_ABSTRACT) != 0)) { 85 String curDetail = obj.getName() + obj.getSignature(); 86 for (String infMethodDetail : interfaceMethods) { 87 if (curDetail.equals(infMethodDetail)) 88 bugReporter.reportBug(new BugInstance(this, "USM_USELESS_ABSTRACT_METHOD", LOW_PRIORITY) 89 .addClassAndMethod(getClassContext().getJavaClass(), obj)); 90 } 91 } 92 super.visitMethod(obj); 93 } 94 95 @Override 96 public void visitCode(Code obj) 97 { 98 try { 99 String methodName = getMethodName(); 100 101 if (!methodName.equals("<init>") 102 && !methodName.equals("clone") 103 && ((getMethod().getAccessFlags() & (Constants.ACC_STATIC|Constants.ACC_SYNTHETIC)) == 0)) { 104 105 106 Attribute[] atts = getMethod().getAttributes(); 107 for (Attribute att : atts) { 108 if (att.getClass().equals(Synthetic.class)) 109 return; 110 } 111 112 byte[] codeBytes = obj.getCode(); 113 if ((codeBytes.length == 0) || (codeBytes[0] != ALOAD_0)) 114 return; 115 116 state = SEEN_NOTHING; 117 invokePC = 0; 118 super.visitCode(obj); 119 if ((state == SEEN_RETURN) && (invokePC != 0)) { 120 Method superMethod = findSuperclassMethod(superclassName, getMethod()); 122 if ((superMethod == null) || accessModifiersAreDifferent(getMethod(), superMethod)) 123 return; 124 125 bugReporter.reportBug( new BugInstance( this, "USM_USELESS_SUBCLASS_METHOD", LOW_PRIORITY ) 126 .addClassAndMethod(this) 127 .addSourceLine(this, invokePC)); 128 } 129 } 130 } 131 catch (ClassNotFoundException cnfe) { 132 bugReporter.reportMissingClass(cnfe); 133 } 134 } 135 136 @Override 137 public void sawOpcode(int seen) { 138 switch (state) { 139 case SEEN_NOTHING: 140 if (seen == ALOAD_0) { 141 argTypes = Type.getArgumentTypes(this.getMethodSig()); 142 curParm = 0; 143 curParmOffset = 1; 144 if (argTypes.length > 0) 145 state = SEEN_PARM; 146 else 147 state = SEEN_LAST_PARM; 148 } else 149 state = SEEN_INVALID; 150 break; 151 152 case SEEN_PARM: 153 if (curParm >= argTypes.length) 154 state = SEEN_INVALID; 155 else { 156 String signature = argTypes[curParm++].getSignature(); 157 char typeChar0 = signature.charAt(0); 158 if ((typeChar0 == 'L') || (typeChar0 == '[')) { 159 checkParm(seen, ALOAD_0, ALOAD, 1); 160 } 161 else if (typeChar0 == 'D') { 162 checkParm(seen, DLOAD_0, DLOAD, 2); 163 } 164 else if (typeChar0 == 'F') { 165 checkParm(seen, FLOAD_0, FLOAD, 1); 166 } 167 else if (typeChar0 == 'I') { 168 checkParm(seen, ILOAD_0, ILOAD, 1); 169 } 170 else if (typeChar0 == 'J') { 171 checkParm(seen, LLOAD_0, LLOAD, 2); 172 } 173 if ((state != SEEN_INVALID) && (curParm >= argTypes.length)) 174 state = SEEN_LAST_PARM; 175 176 } 177 break; 178 179 case SEEN_LAST_PARM: 180 if ((seen == INVOKENONVIRTUAL) && getMethodName().equals(getNameConstantOperand()) && getMethodSig().equals(getSigConstantOperand())) { 181 invokePC = getPC(); 182 state = SEEN_INVOKE; 183 } 184 else 185 state = SEEN_INVALID; 186 break; 187 188 case SEEN_INVOKE: 189 Type returnType = getMethod().getReturnType(); 190 char retSigChar0 = returnType.getSignature().charAt(0); 191 if ((retSigChar0 == 'V') && (seen == RETURN)) 192 state = SEEN_RETURN; 193 else if (((retSigChar0 == 'L') || (retSigChar0 == '[')) && (seen == ARETURN)) 194 state = SEEN_RETURN; 195 else if ((retSigChar0 == 'D') && (seen == DRETURN)) 196 state = SEEN_RETURN; 197 else if ((retSigChar0 == 'F') && (seen == FRETURN)) 198 state = SEEN_RETURN; 199 else if ((retSigChar0 == 'I') && (seen == IRETURN)) 200 state = SEEN_RETURN; 201 else if ((retSigChar0 == 'J') && (seen == LRETURN)) 202 state = SEEN_RETURN; 203 else 204 state = SEEN_INVALID; 205 break; 206 207 case SEEN_RETURN: 208 state = SEEN_INVALID; 209 break; 210 } 211 } 212 213 private void checkParm(int seen, int fastOpBase, int slowOp, int parmSize) { 214 if ((curParmOffset >= 1) && (curParmOffset <= 3)) { 215 if (seen == (fastOpBase + curParmOffset)) 216 curParmOffset += parmSize; 217 else 218 state = SEEN_INVALID; 219 } 220 else if (curParmOffset == 0) 221 state = SEEN_INVALID; 222 else if ((seen == slowOp) && (getRegisterOperand() == curParmOffset)) 223 curParmOffset += parmSize; 224 else 225 state = SEEN_INVALID; 226 } 227 228 private Method findSuperclassMethod(String superclassName, Method subclassMethod) 229 throws ClassNotFoundException { 230 231 String methodName = subclassMethod.getName(); 232 Type[] subArgs = null; 233 JavaClass superClass = Repository.lookupClass(superclassName); 234 Method[] methods = superClass.getMethods(); 235 outer: 236 for (Method m : methods) { 237 if (m.getName().equals(methodName)) { 238 if (subArgs == null) 239 subArgs = Type.getArgumentTypes(subclassMethod.getSignature()); 240 Type[] superArgs = Type.getArgumentTypes(m.getSignature()); 241 if (subArgs.length == superArgs.length) { 242 for (int j = 0; j < subArgs.length; j++) { 243 if (!superArgs[j].equals(subArgs[j])) 244 continue outer; 245 } 246 return m; 247 } 248 } 249 } 250 251 if(!superclassName.equals("Object")) { 252 String superSuperClassName = superClass.getSuperclassName(); 253 if (superSuperClassName.equals(superclassName)) { 254 throw new ClassNotFoundException ( 255 "superclass of " + superclassName + " is itself"); 256 } 257 return findSuperclassMethod(superClass.getSuperclassName(), subclassMethod); 258 } 259 260 return null; 261 } 262 263 private boolean accessModifiersAreDifferent(Method m1, Method m2) { 264 int access1 = m1.getAccessFlags() & (Constants.ACC_PRIVATE|Constants.ACC_PROTECTED|Constants.ACC_PUBLIC); 265 int access2 = m2.getAccessFlags() & (Constants.ACC_PRIVATE|Constants.ACC_PROTECTED|Constants.ACC_PUBLIC); 266 267 return access1 != access2; 268 } 269 } 270 | Popular Tags |