1 19 20 package edu.umd.cs.findbugs.ba; 21 22 import java.util.HashMap ; 23 import java.util.Map ; 24 25 import org.apache.bcel.Constants; 26 import org.apache.bcel.Repository; 27 import org.apache.bcel.classfile.Code; 28 import org.apache.bcel.classfile.ConstantClass; 29 import org.apache.bcel.classfile.ConstantFieldref; 30 import org.apache.bcel.classfile.ConstantNameAndType; 31 import org.apache.bcel.classfile.ConstantPool; 32 import org.apache.bcel.classfile.JavaClass; 33 import org.apache.bcel.classfile.Method; 34 import org.apache.bcel.generic.ConstantPoolGen; 35 import org.apache.bcel.generic.INVOKESTATIC; 36 37 import edu.umd.cs.findbugs.SystemProperties; 38 39 53 public class InnerClassAccessMap { 54 private static final boolean DEBUG = SystemProperties.getBoolean("icam.debug"); 55 56 59 60 64 private Map <String , Map <String , InnerClassAccess>> classToAccessMap; 65 66 69 70 75 public static InnerClassAccessMap create() { 76 return new InnerClassAccessMap(); 77 } 78 79 87 public InnerClassAccess getInnerClassAccess(String className, String methodName) throws ClassNotFoundException { 88 Map <String , InnerClassAccess> map = getAccessMapForClass(className); 89 return map.get(methodName); 90 } 91 92 100 public InnerClassAccess getInnerClassAccess(INVOKESTATIC inv, ConstantPoolGen cpg) throws ClassNotFoundException { 101 String methodName = inv.getMethodName(cpg); 102 if (methodName.startsWith("access$")) { 103 String className = inv.getClassName(cpg); 104 105 return getInnerClassAccess(className, methodName); 106 } 107 return null; 108 } 109 110 113 public void clearCache() { 114 classToAccessMap.clear(); 115 } 116 117 120 121 124 private InnerClassAccessMap() { 125 this.classToAccessMap = new HashMap <String , Map <String , InnerClassAccess>>(); 126 } 127 128 131 private static int toInt(byte b) { 132 int value = b & 0x7F; 133 if ((b & 0x80) != 0) 134 value |= 0x80; 135 return value; 136 } 137 138 141 private static int getIndex(byte[] instructionList, int index) { 142 return (toInt(instructionList[index + 1]) << 8) | toInt(instructionList[index + 2]); 143 } 144 145 private static class LookupFailure extends RuntimeException { 146 149 private static final long serialVersionUID = 1L; 150 private final ClassNotFoundException exception; 151 152 public LookupFailure(ClassNotFoundException exception) { 153 this.exception = exception; 154 } 155 156 public ClassNotFoundException getException() { 157 return exception; 158 } 159 } 160 161 165 private static class InstructionCallback implements BytecodeScanner.Callback { 166 private JavaClass javaClass; 167 private String methodName; 168 private String methodSig; 169 private byte[] instructionList; 170 private InnerClassAccess access; 171 private int accessCount; 172 173 181 public InstructionCallback(JavaClass javaClass, String methodName, String methodSig, byte[] instructionList) { 182 this.javaClass = javaClass; 183 this.methodName = methodName; 184 this.methodSig = methodSig; 185 this.instructionList = instructionList; 186 this.access = null; 187 this.accessCount = 0; 188 } 189 190 public void handleInstruction(int opcode, int index) { 191 switch (opcode) { 192 case Constants.GETFIELD: 193 case Constants.PUTFIELD: 194 setField(getIndex(instructionList, index), false, opcode == Constants.GETFIELD); 195 break; 196 case Constants.GETSTATIC: 197 case Constants.PUTSTATIC: 198 setField(getIndex(instructionList, index), true, opcode == Constants.GETSTATIC); 199 break; 200 } 201 } 202 203 210 public InnerClassAccess getAccess() { 211 return access; 212 } 213 214 221 private void setField(int cpIndex, boolean isStatic, boolean isLoad) { 222 accessCount++; 224 if (accessCount != 1) { 225 access = null; 226 return; 227 } 228 229 ConstantPool cp = javaClass.getConstantPool(); 230 ConstantFieldref fieldref = (ConstantFieldref) cp.getConstant(cpIndex); 231 232 ConstantClass cls = (ConstantClass) cp.getConstant(fieldref.getClassIndex()); 233 String className = cls.getBytes(cp).replace('/', '.'); 234 235 ConstantNameAndType nameAndType = (ConstantNameAndType) cp.getConstant(fieldref.getNameAndTypeIndex()); 236 String fieldName = nameAndType.getName(cp); 237 String fieldSig = nameAndType.getSignature(cp); 238 239 try { 240 XField xfield = Hierarchy.findXField(className, fieldName, fieldSig); 241 if (xfield != null && xfield.isStatic() == isStatic && isValidAccessMethod(methodSig, xfield, isLoad)) { 242 access = new InnerClassAccess(methodName, methodSig, xfield, isLoad); 243 } 244 } catch (ClassNotFoundException e) { 245 throw new LookupFailure(e); 246 } 247 } 248 249 258 private boolean isValidAccessMethod(String methodSig, XField field, boolean isLoad) { 259 260 int paramsEnd = methodSig.indexOf(')'); 263 if (paramsEnd < 0) 264 return false; 265 String methodParams = methodSig.substring(0, paramsEnd + 1); 266 String methodReturnType = methodSig.substring(paramsEnd + 1); 267 268 String classSig = "L" + javaClass.getClassName().replace('.', '/') + ";"; 270 StringBuffer buf = new StringBuffer (); 271 buf.append('('); 272 if (!field.isStatic()) 273 buf.append(classSig); if (!isLoad) 275 buf.append(field.getSignature()); buf.append(')'); 277 String expectedMethodParams = buf.toString(); 278 279 if (!methodParams.equals(expectedMethodParams)) { 281 if (DEBUG) { 282 System.out.println("In " + javaClass.getClassName() + "." + methodName + 283 " expected params " + 284 expectedMethodParams + ", saw " + methodParams); 285 System.out.println(isLoad ? "LOAD" : "STORE"); 286 } 287 return false; 288 } 289 290 if (!methodReturnType.equals("V") && !methodReturnType.equals(field.getSignature())) { 292 if (DEBUG) { 293 System.out.println("In " + javaClass.getClassName() + "." + methodName + 294 " expected return type V or " + field.getSignature() + 295 ", saw " + methodReturnType); 296 System.out.println(isLoad ? "LOAD" : "STORE"); 297 } 298 return false; 299 } 300 301 return true; 302 } 303 } 304 305 private static final Map <String , InnerClassAccess> emptyMap = new HashMap <String , InnerClassAccess>(); 306 307 314 private Map <String , InnerClassAccess> getAccessMapForClass(String className) 315 throws ClassNotFoundException { 316 317 Map <String , InnerClassAccess> map = classToAccessMap.get(className); 318 if (map == null) { 319 map = new HashMap <String , InnerClassAccess>(); 320 321 if (!className.startsWith("[")) { 322 JavaClass javaClass = Repository.lookupClass(className); 323 324 Method[] methodList = javaClass.getMethods(); 325 for (Method method : methodList) { 326 String methodName = method.getName(); 327 if (!methodName.startsWith("access$")) 328 continue; 329 330 Code code = method.getCode(); 331 if (code == null) 332 continue; 333 334 if (DEBUG) System.out.println("Analyzing " + className + "." + 335 method.getName() + " as an inner-class access method..."); 336 337 byte[] instructionList = code.getCode(); 338 String methodSig = method.getSignature(); 339 InstructionCallback callback = new InstructionCallback(javaClass, methodName, methodSig, instructionList); 340 try { 341 new BytecodeScanner().scan(instructionList, callback); 342 } catch (LookupFailure lf) { 343 throw lf.getException(); 344 } 345 InnerClassAccess access = callback.getAccess(); 346 if (DEBUG) System.out.println((access != null ? "IS" : "IS NOT") + " an inner-class access method"); 347 if (access != null) 348 map.put(methodName, access); 349 } 350 } 351 352 if (map.size() == 0) 353 map = emptyMap; 354 355 classToAccessMap.put(className, map); 356 } 357 358 return map; 359 } 360 361 } 362 363 | Popular Tags |