OLD | NEW |
(Empty) | |
| 1 /* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved |
| 2 * |
| 3 * This program is distributed under the terms of |
| 4 * the GNU General Public License Version 2. See the LICENSE file |
| 5 * at the top of the source tree. |
| 6 */ |
| 7 package org.pantsbuild.jmake; |
| 8 |
| 9 import java.io.Serializable; |
| 10 import java.lang.reflect.Modifier; |
| 11 import java.util.ArrayList; |
| 12 import java.util.LinkedHashSet; |
| 13 import java.util.List; |
| 14 import java.util.Set; |
| 15 |
| 16 /** |
| 17 * A reflection of a class, in the form that allows fast checks and information
obtaining. |
| 18 * |
| 19 * @author Misha Dmitriev |
| 20 * 5 April 2004 |
| 21 */ |
| 22 @SuppressWarnings("serial") |
| 23 public class ClassInfo implements Serializable { |
| 24 |
| 25 public static final int VER_OLD = 0; // Old version |
| 26 public static final int VER_NEW = 1; // New version |
| 27 public static final int NO_VERSIONS = 2; // Non-project class, no change tr
acking |
| 28 private transient PCDManager pcdm; |
| 29 transient int verCode; // Version code for this Cla
ssInfo - one of the above. |
| 30 String name = null; |
| 31 transient String packageName; // Package name; restored wh
en database is reloaded |
| 32 int javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_OLDEST; // Can have valu
es from Utils.JAVAC_TARGET_RELEASE_xxx |
| 33 String cpoolRefsToClasses[]; // Directly referenced class names tri
mmed of array and 'L' prefixes and ';' suffixes |
| 34 boolean isRefClassArray[]; // Indicates if a directly referenced
class is actually an array class |
| 35 // In all signatures we replace the 'L' and ';' symbols that enclose non-pri
mitive type names with '@' and '#' respectively, |
| 36 // so that class names inside signatures can be located fast and unambiguous
ly. |
| 37 String cpoolRefsToFieldClasses[]; // Defining classes of referenced fiel
ds, trimmed of enclosing 'L' and ';' symbols |
| 38 String cpoolRefsToFieldNames[]; // Names of referenced fields |
| 39 String cpoolRefsToFieldSignatures[]; // Signatures of referenced fields |
| 40 String cpoolRefsToMethodClasses[]; // Defining classes of referenced meth
ods, trimmed of enclosing 'L' and ';' symbols |
| 41 String cpoolRefsToMethodNames[]; // Names of referenced methods |
| 42 String cpoolRefsToMethodSignatures[]; // Signatures of referenced methods |
| 43 char accessFlags; // isInterface flag included |
| 44 boolean isNonMemberNestedClass = false; // True if this is a non-member nest
ed class |
| 45 String superName; |
| 46 String interfaces[]; |
| 47 String fieldNames[]; |
| 48 String fieldSignatures[]; |
| 49 char fieldAccessFlags[]; |
| 50 Object primitiveConstantInitValues[]; |
| 51 String methodNames[]; |
| 52 String methodSignatures[]; |
| 53 char methodAccessFlags[]; |
| 54 String checkedExceptions[][]; |
| 55 transient ClassInfo directSubclasses[]; // Direct subclasses. Created
lazily and not preserved on disk. |
| 56 transient String directlyEnclosingClass; // Directly enclosing class na
me; restored when database is reloaded |
| 57 transient String topLevelEnclosingClass; // Top-level enclosing class n
ame; restored when database is reloaded |
| 58 String nestedClasses[]; // Names of all nested classes. Don't ma
ke transient - it's used to check |
| 59 // if nested classes for this class were added/deleted in new version |
| 60 transient char nestedClassAccessFlags[]; // No need to store this infor
mation permanently |
| 61 transient boolean nestedClassNonMember[]; // Ditto |
| 62 |
| 63 /** Creates new ClassInfo out of a class file. The last parameter is needed
only to produce sensible error reports.*/ |
| 64 public ClassInfo(byte[] classFileBytes, int verCode, PCDManager pcdm, String
classFileFullPath) { |
| 65 this.pcdm = pcdm; |
| 66 this.verCode = verCode; |
| 67 pcdm.getClassFileReader().readClassFile(classFileBytes, this, classFileF
ullPath, true); |
| 68 packageName = Utils.getPackageName(name); |
| 69 directlyEnclosingClass = |
| 70 Utils.getDirectlyEnclosingClass(name, this.javacTargetRelease); |
| 71 topLevelEnclosingClass = Utils.getTopLevelEnclosingClass(name); |
| 72 } |
| 73 |
| 74 /** |
| 75 * Create a "lightweight" ClassInfo, that contains just the class name, supe
r name, interfaces, flags and verCode. |
| 76 * Used for non-project classes, that don't change themselves, for which we
are only interested in type hierarchy structure. |
| 77 */ |
| 78 public ClassInfo(byte[] classFileBytes, PCDManager pcdm, String classFileFul
lPath) { |
| 79 this.pcdm = pcdm; |
| 80 this.verCode = NO_VERSIONS; |
| 81 pcdm.getClassFileReader().readClassFile(classFileBytes, this, classFileF
ullPath, false); |
| 82 packageName = Utils.getPackageName(name); |
| 83 directlyEnclosingClass = |
| 84 Utils.getDirectlyEnclosingClass(name, this.javacTargetRelease); |
| 85 topLevelEnclosingClass = Utils.getTopLevelEnclosingClass(name); |
| 86 } |
| 87 |
| 88 /** Even more lightweight variant - created for a deleted non-project class,
to enable minimum possible checks. */ |
| 89 public ClassInfo(String name, PCDManager pcdm) { |
| 90 this.pcdm = pcdm; |
| 91 this.verCode = NO_VERSIONS; |
| 92 this.name = name; |
| 93 packageName = Utils.getPackageName(name); |
| 94 directlyEnclosingClass = Utils.getDirectlyEnclosingClass(name, 0); |
| 95 topLevelEnclosingClass = Utils.getTopLevelEnclosingClass(name); |
| 96 } |
| 97 |
| 98 public ClassInfo() { |
| 99 } |
| 100 |
| 101 /** Initialize transient data that can be initialized immediately after this
ClassInfo is read from the project database */ |
| 102 public void initializeImmediateTransientFields() { |
| 103 verCode = VER_OLD; |
| 104 |
| 105 packageName = Utils.getPackageName(name); |
| 106 |
| 107 directlyEnclosingClass = |
| 108 Utils.getDirectlyEnclosingClass(name, this.javacTargetRelease); |
| 109 topLevelEnclosingClass = Utils.getTopLevelEnclosingClass(name); |
| 110 } |
| 111 |
| 112 /** |
| 113 * Called to restore the pointer to the current PCDManager after this ClassI
nfo is brought back |
| 114 * from the store. |
| 115 */ |
| 116 public void restorePCDM(PCDManager pcdm) { |
| 117 this.pcdm = pcdm; |
| 118 } |
| 119 |
| 120 public boolean isInterface() { |
| 121 return Modifier.isInterface(accessFlags); |
| 122 } |
| 123 |
| 124 public boolean isAbstract() { |
| 125 return Modifier.isAbstract(accessFlags); |
| 126 } |
| 127 |
| 128 public boolean isPublic() { |
| 129 return Modifier.isPublic(accessFlags); |
| 130 } |
| 131 |
| 132 /** |
| 133 * Returns the names of the superclasses of the given class (transitively),
that belong |
| 134 * to the same project, plus those of the superclasses that can be found on
the class path |
| 135 * supplied to jmake, and on the boot class path. |
| 136 */ |
| 137 public List<String> getAllSuperclassNames() { |
| 138 List<String> res = new ArrayList<String>(); |
| 139 String superName = this.superName; |
| 140 while (superName != null && !"java/lang/Object".equals(superName)) { |
| 141 res.add(superName); |
| 142 ClassInfo classInfo = pcdm.getClassInfoForName(verCode, superName); |
| 143 if (classInfo == null) { // Class not in project (or deleted?). Try
to find it and further superclasses in non-project classes |
| 144 ClassPath.getSuperclasses(superName, res, pcdm); |
| 145 break; |
| 146 } |
| 147 superName = classInfo.superName; |
| 148 } |
| 149 return res; |
| 150 } |
| 151 |
| 152 /** |
| 153 * Returns the set of names of the interfaces transitively implemented by th
e given |
| 154 * class, that belong to the same project. |
| 155 */ |
| 156 public Set<String> getAllImplementedIntfNames() { |
| 157 Set<String> res = new LinkedHashSet<String>(); |
| 158 addImplementedInterfaceNames(false, res); |
| 159 return res; |
| 160 } |
| 161 |
| 162 /** Add to the given set the names of direct/all interfaces implemented by t
he given class. */ |
| 163 private void addImplementedInterfaceNames(boolean directOnly, |
| 164 Set<String> intfSet) { |
| 165 if (interfaces != null) { |
| 166 for (int i = 0; i < interfaces.length; i++) { |
| 167 String superIntfName = interfaces[i]; |
| 168 intfSet.add(superIntfName); |
| 169 if (directOnly) { |
| 170 continue; |
| 171 } |
| 172 ClassInfo superIntfInfo = |
| 173 pcdm.getClassInfoForName(verCode, superIntfName); |
| 174 if (superIntfInfo == null) { // Class not in project |
| 175 ClassPath.addAllImplementedInterfaceNames(superIntfName, int
fSet, pcdm); |
| 176 } else { |
| 177 superIntfInfo.addImplementedInterfaceNames(false, intfSet); |
| 178 } |
| 179 } |
| 180 } |
| 181 |
| 182 if (directOnly || superName == null || |
| 183 "java/lang/Object".equals(superName)) { |
| 184 return; |
| 185 } |
| 186 ClassInfo superInfo = pcdm.getClassInfoForName(verCode, superName); |
| 187 if (superInfo == null) { // Class not in project |
| 188 ClassPath.addAllImplementedInterfaceNames(superName, intfSet, pcdm); |
| 189 } else { |
| 190 superInfo.addImplementedInterfaceNames(false, intfSet); |
| 191 } |
| 192 } |
| 193 |
| 194 /** Returns the array of all direct subclasses of this class (array of zero
length if there are none). */ |
| 195 public ClassInfo[] getDirectSubclasses() { |
| 196 if (directSubclasses != null) { |
| 197 return directSubclasses; |
| 198 } |
| 199 |
| 200 List<ClassInfo> listRes = new ArrayList<ClassInfo>(); |
| 201 |
| 202 for (PCDEntry entry : pcdm.entries()) { |
| 203 ClassInfo classInfo = pcdm.getClassInfoForPCDEntry(verCode, entry); |
| 204 if (classInfo == null) { |
| 205 continue; // New or deleted class, depending on verCode |
| 206 } |
| 207 if (classInfo.superName.equals(name)) { |
| 208 listRes.add(classInfo); |
| 209 } |
| 210 } |
| 211 |
| 212 directSubclasses = listRes.toArray(new ClassInfo[listRes.size()]); |
| 213 return directSubclasses; |
| 214 } |
| 215 |
| 216 /** Check if the initial values for the given primitive constatnts in two cl
asses are the same. */ |
| 217 public static boolean constFieldInitValuesEqual(ClassInfo oldClassInfo, int
oldFieldNo, |
| 218 ClassInfo newClassInfo, int newFieldNo) { |
| 219 Object oldInitValue = oldClassInfo.primitiveConstantInitValues == null ?
null |
| 220 : oldClassInfo.primitiveConstantInitValues[oldFieldNo]; |
| 221 Object newInitValue = newClassInfo.primitiveConstantInitValues == null ?
null |
| 222 : newClassInfo.primitiveConstantInitValues[newFieldNo]; |
| 223 if (oldInitValue == newInitValue) { |
| 224 return true; |
| 225 } |
| 226 if (oldInitValue == null || newInitValue == null) { |
| 227 return false; |
| 228 } |
| 229 |
| 230 if (oldInitValue instanceof Integer) { |
| 231 if (((Integer) oldInitValue).intValue() == ((Integer) newInitValue).
intValue()) { |
| 232 return true; |
| 233 } else { |
| 234 return false; |
| 235 } |
| 236 } else if (oldInitValue instanceof String) { |
| 237 if ( ((String) oldInitValue).equals((String) newInitValue) ) { |
| 238 return true; |
| 239 } else { |
| 240 return false; |
| 241 } |
| 242 } else if (oldInitValue instanceof Long) { |
| 243 if (((Long) oldInitValue).longValue() == ((Long) newInitValue).longV
alue()) { |
| 244 return true; |
| 245 } else { |
| 246 return false; |
| 247 } |
| 248 } else if (oldInitValue instanceof Float) { |
| 249 if (((Float) oldInitValue).floatValue() == ((Float) newInitValue).fl
oatValue()) { |
| 250 return true; |
| 251 } else { |
| 252 return false; |
| 253 } |
| 254 } else if (oldInitValue instanceof Double) { |
| 255 if (((Double) oldInitValue).doubleValue() == ((Double) newInitValue)
.doubleValue()) { |
| 256 return true; |
| 257 } else { |
| 258 return false; |
| 259 } |
| 260 } |
| 261 |
| 262 return true; |
| 263 } |
| 264 |
| 265 public boolean implementsInterfaceDirectly(String intfName) { |
| 266 if (interfaces == null) { |
| 267 return false; |
| 268 } |
| 269 for (int i = 0; i < interfaces.length; i++) { |
| 270 if (intfName.equals(interfaces[i])) { |
| 271 return true; |
| 272 } |
| 273 } |
| 274 return false; |
| 275 } |
| 276 |
| 277 /** Check if this class implements interface I or any subinterface of I dire
ctly */ |
| 278 public boolean implementsIntfOrSubintfDirectly(String intfName) { |
| 279 if (interfaces == null) { |
| 280 return false; |
| 281 } |
| 282 for (int i = 0; i < interfaces.length; i++) { |
| 283 if (intfName.equals(interfaces[i])) { |
| 284 return true; |
| 285 } |
| 286 // An interface can have multiple superinterfaces, all of which are
listed in its "interfaces" array |
| 287 // (although in the .java source it "extends" them all). |
| 288 ClassInfo superIntfInfo = |
| 289 pcdm.getClassInfoForName(verCode, interfaces[i]); |
| 290 if (superIntfInfo == null) { |
| 291 continue; // Class not in project |
| 292 } |
| 293 if (superIntfInfo.implementsIntfOrSubintfDirectly(intfName)) { |
| 294 return true; |
| 295 } |
| 296 } |
| 297 return false; |
| 298 } |
| 299 |
| 300 /** |
| 301 * Class C implements interface I indirectly, if C or some superclass of C d
irectly implements I |
| 302 * or some subinterface of I. |
| 303 */ |
| 304 public boolean implementsInterfaceDirectlyOrIndirectly(String intfName) { |
| 305 if (interfaces == null) { |
| 306 return false; |
| 307 } |
| 308 |
| 309 if (implementsIntfOrSubintfDirectly(intfName)) { |
| 310 return true; |
| 311 } |
| 312 |
| 313 if (superName != null) { |
| 314 ClassInfo superInfo = pcdm.getClassInfoForName(verCode, superName); |
| 315 if (superInfo == null) { |
| 316 return false; // Class not in project |
| 317 } |
| 318 return superInfo.implementsInterfaceDirectlyOrIndirectly(intfName); |
| 319 } |
| 320 |
| 321 return false; |
| 322 } |
| 323 |
| 324 /** |
| 325 * Returns true if this class declares a field with the same name and type a
s |
| 326 * the field number fieldNo in class classInfo. |
| 327 */ |
| 328 public boolean declaresField(ClassInfo classInfo, int fieldNo) { |
| 329 if (fieldNames == null) { |
| 330 return false; |
| 331 } |
| 332 String fieldName = classInfo.fieldNames[fieldNo]; |
| 333 String fieldSignature = classInfo.fieldSignatures[fieldNo]; |
| 334 |
| 335 for (int i = 0; i < fieldNames.length; i++) { |
| 336 if (fieldName.equals(fieldNames[i]) && |
| 337 fieldSignature.equals(fieldSignatures[i])) { |
| 338 return true; |
| 339 } |
| 340 } |
| 341 return false; |
| 342 } |
| 343 |
| 344 /** Returns true if this class declares a field with the given name, signatu
re and access */ |
| 345 public boolean declaresField(String name, String signature, boolean isStatic
) { |
| 346 if (fieldNames == null) { |
| 347 return false; |
| 348 } |
| 349 signature = ("@" + signature + "#").intern(); |
| 350 for (int i = 0; i < fieldNames.length; i++) { |
| 351 if (name.equals(fieldNames[i]) && |
| 352 signature.equals(fieldSignatures[i]) && |
| 353 Modifier.isStatic(fieldAccessFlags[i]) == isStatic) { |
| 354 return true; |
| 355 } |
| 356 } |
| 357 return false; |
| 358 } |
| 359 |
| 360 /** |
| 361 * Returns true if this class declares a method with the same name and signa
ture as |
| 362 * the method number methodNo in class classInfo. |
| 363 */ |
| 364 public boolean declaresMethod(ClassInfo classInfo, int methodNo) { |
| 365 if (methodNames == null) { |
| 366 return false; |
| 367 } |
| 368 String methodName = classInfo.methodNames[methodNo]; |
| 369 String methodSignature = classInfo.methodSignatures[methodNo]; |
| 370 |
| 371 for (int i = 0; i < methodNames.length; i++) { |
| 372 if (methodName.equals(methodNames[i]) && |
| 373 methodSignature.equals(methodSignatures[i])) { |
| 374 return true; |
| 375 } |
| 376 } |
| 377 return false; |
| 378 } |
| 379 |
| 380 /** |
| 381 * If this class declares a method with the same name and signature as the g
iven method, |
| 382 * return its position. Otherwise, return -1. |
| 383 */ |
| 384 public int getDeclaredMethodPos(ClassInfo classInfo, int methodNo) { |
| 385 if (methodNames == null) { |
| 386 return -1; |
| 387 } |
| 388 String methodName = classInfo.methodNames[methodNo]; |
| 389 String methodSignature = classInfo.methodSignatures[methodNo]; |
| 390 |
| 391 for (int i = 0; i < methodNames.length; i++) { |
| 392 if (methodName.equals(methodNames[i]) && |
| 393 methodSignature.equals(methodSignatures[i])) { |
| 394 return i; |
| 395 } |
| 396 } |
| 397 return -1; |
| 398 } |
| 399 |
| 400 /** |
| 401 * Returns a nonnegative number (position in the method array) if this class
declares a method with the |
| 402 * name methodName, and -1 otherwise. |
| 403 */ |
| 404 public int declaresSameNameMethod(String methodName) { |
| 405 if (methodNames == null) { |
| 406 return -1; |
| 407 } |
| 408 for (int j = 0; j < methodNames.length; j++) { |
| 409 if (methodName.equals(methodNames[j])) { |
| 410 return j; |
| 411 } |
| 412 } |
| 413 return -1; |
| 414 } |
| 415 |
| 416 /** |
| 417 * Check if this class references the given class in different ways, dependi
ng on thorDegree parameter. |
| 418 * thorDegree = 0: the given class (but not its array class) directly from t
he constantpool. |
| 419 * |
| 420 * thorDegree = 1: the given class or its array class directly from the cons
tantpool, as a |
| 421 * type of a data field, as a type in a method signature or a thrown excepti
on, as a directly |
| 422 * implemented interface or a direct superclass |
| 423 * |
| 424 * thorDegree = 2: the given class or its array class directly or indirectly
from the |
| 425 * constantpool, as a type of a data field, as a type in a method signature
or a thrown exception, |
| 426 * as a directly/indirectly implemented interface or a direct/indirect super
class. |
| 427 * |
| 428 * isRefTypeInterface indicates whether className is an interface. |
| 429 */ |
| 430 public boolean referencesClass(String className, boolean isRefTypeInterface,
int thorDegree) { |
| 431 int i; |
| 432 |
| 433 if (thorDegree == 0) { |
| 434 if (cpoolRefsToClasses == null) { |
| 435 return false; |
| 436 } |
| 437 for (i = 0; i < cpoolRefsToClasses.length; i++) { |
| 438 if (!isRefClassArray[i] && |
| 439 className.equals(cpoolRefsToClasses[i])) { |
| 440 return true; |
| 441 } |
| 442 } |
| 443 } else { |
| 444 if (isSubclassOf(className, (thorDegree == 1))) { |
| 445 return true; |
| 446 } |
| 447 if (isRefTypeInterface) { |
| 448 if (thorDegree == 1) { |
| 449 if (implementsInterfaceDirectly(className)) { |
| 450 return true; |
| 451 } |
| 452 } else { |
| 453 // Check for indirectly implemented interfaces |
| 454 if (implementsInterfaceDirectlyOrIndirectly(className)) { |
| 455 return true; |
| 456 } |
| 457 } |
| 458 } |
| 459 |
| 460 if (cpoolRefsToClasses != null) { |
| 461 for (i = 0; i < cpoolRefsToClasses.length; i++) { |
| 462 if (className.equals(cpoolRefsToClasses[i])) { |
| 463 return true; |
| 464 } |
| 465 } |
| 466 } |
| 467 if (thorDegree == 2) { |
| 468 // Check for indirect references from the constantpool |
| 469 if (cpoolRefsToFieldSignatures != null) { |
| 470 for (i = 0; i < cpoolRefsToFieldSignatures.length; i++) { |
| 471 if (signatureIncludesClassName(cpoolRefsToFieldSignature
s[i], className)) { |
| 472 return true; |
| 473 } |
| 474 } |
| 475 } |
| 476 if (cpoolRefsToMethodNames != null) { |
| 477 for (i = 0; i < cpoolRefsToMethodSignatures.length; i++) { |
| 478 if (signatureIncludesClassName(cpoolRefsToMethodSignatur
es[i], className)) { |
| 479 return true; |
| 480 } |
| 481 } |
| 482 } |
| 483 } |
| 484 |
| 485 if (fieldSignatures != null) { |
| 486 for (i = 0; i < fieldSignatures.length; i++) { |
| 487 if (signatureIncludesClassName(fieldSignatures[i], className
)) { |
| 488 return true; |
| 489 } |
| 490 } |
| 491 } |
| 492 if (methodSignatures != null) { |
| 493 for (i = 0; i < methodSignatures.length; i++) { |
| 494 if (signatureIncludesClassName(methodSignatures[i], classNam
e)) { |
| 495 return true; |
| 496 } |
| 497 } |
| 498 } |
| 499 if (checkedExceptions != null) { |
| 500 for (i = 0; i < checkedExceptions.length; i++) { |
| 501 if (checkedExceptions[i] != null) { |
| 502 String excArray[] = checkedExceptions[i]; |
| 503 for (int j = 0; j < excArray.length; j++) { |
| 504 if (className.equals(excArray[j])) { |
| 505 return true; |
| 506 } |
| 507 } |
| 508 } |
| 509 } |
| 510 } |
| 511 } |
| 512 |
| 513 return false; |
| 514 } |
| 515 |
| 516 private static boolean signatureIncludesClassName(String signature, String c
lassName) { |
| 517 int stIndex = signature.indexOf(className); |
| 518 if (stIndex == -1) { |
| 519 return false; |
| 520 } |
| 521 return ((stIndex != 0 && signature.charAt(stIndex - 1) == '@' && signatu
re.charAt(stIndex + className.length()) == '#') || |
| 522 (stIndex == 0 && signature.length() == className.length())); |
| 523 } |
| 524 |
| 525 public boolean isSubclassOf(String className, boolean directOnly) { |
| 526 if (className.equals(superName)) { |
| 527 return true; |
| 528 } |
| 529 if (directOnly) { |
| 530 return false; |
| 531 } |
| 532 String superName = this.superName; |
| 533 while (superName != null) { |
| 534 if (className.equals(superName)) { |
| 535 return true; |
| 536 } |
| 537 ClassInfo classInfo = pcdm.getClassInfoForName(verCode, superName); |
| 538 if (classInfo == null) { |
| 539 break; // Class not in project |
| 540 } |
| 541 superName = classInfo.superName; |
| 542 } |
| 543 return false; |
| 544 } |
| 545 |
| 546 /** |
| 547 * Check if this class references field number fieldNo of class fieldDefClas
sInfo. Let us call |
| 548 * this field C.f. Actual reference contained in the constant pool may be no
t to C.f itself, |
| 549 * but to Csub.f, where Csub is some subclass of C such that neither Csub no
r any other class |
| 550 * located between C and Csub in the class hierarchy redeclares f. We look u
p both "real" |
| 551 * references C.f and "fake" references such as Csub.f. |
| 552 */ |
| 553 public boolean referencesField(ClassInfo fieldDefClassInfo, int fieldNo) { |
| 554 if (cpoolRefsToFieldNames == null) { |
| 555 return false; |
| 556 } |
| 557 String fieldDefClassName = fieldDefClassInfo.name; |
| 558 String fieldName = fieldDefClassInfo.fieldNames[fieldNo]; |
| 559 String fieldSig = fieldDefClassInfo.fieldSignatures[fieldNo]; |
| 560 for (int i = 0; i < cpoolRefsToFieldNames.length; i++) { |
| 561 if (fieldName.equals(cpoolRefsToFieldNames[i]) && |
| 562 fieldSig.equals(cpoolRefsToFieldSignatures[i]) ) { |
| 563 if (fieldDefClassName.equals(cpoolRefsToFieldClasses[i]) ) { |
| 564 return true; // "real" reference |
| 565 } else { // Check if this is a "fake" reference that resolves t
o the above "real" reference |
| 566 ClassInfo classInThisCpool = |
| 567 pcdm.getClassInfoForName(verCode, cpoolRefsToFieldCl
asses[i]); |
| 568 if (classInThisCpool == null) { |
| 569 continue; // Class not in project |
| 570 } |
| 571 if (!classInThisCpool.isSubclassOf(fieldDefClassInfo.name, f
alse)) { |
| 572 continue; |
| 573 } |
| 574 |
| 575 // Ok, now check that this field is not actually redeclared
in fieldDefClassInfo or |
| 576 // somewhere in between it and classInThisCpool |
| 577 boolean redeclared = false; |
| 578 ClassInfo curClass = classInThisCpool; |
| 579 do { |
| 580 if (curClass.declaresField(fieldDefClassInfo, fieldNo))
{ |
| 581 redeclared = true; |
| 582 break; |
| 583 } |
| 584 String superName = curClass.superName; |
| 585 curClass = pcdm.getClassInfoForName(verCode, superName); |
| 586 if (curClass == null) { |
| 587 break; |
| 588 } |
| 589 } while (curClass != fieldDefClassInfo); |
| 590 if (!redeclared) { |
| 591 return true; |
| 592 } |
| 593 } |
| 594 } |
| 595 } |
| 596 return false; |
| 597 } |
| 598 |
| 599 /** |
| 600 * Check if this class references method number methodNo of class methodDefC
lassInfo. Let us |
| 601 * call this method C.m. Actual reference contained in the constant pool may
be not to C.m |
| 602 * itself, but to Csub.m, where Csub is some subclass of C such that neither
Csub nor any |
| 603 * other class located between C and Csub in the class hierarchy redeclares
m. We look up |
| 604 * both "real" references C.m and "fake" references such as Csub.m. |
| 605 */ |
| 606 public boolean referencesMethod(ClassInfo methodDefClassInfo, int methodNo)
{ |
| 607 if (cpoolRefsToMethodNames == null) { |
| 608 return false; |
| 609 } |
| 610 String methodDefClassName = methodDefClassInfo.name; |
| 611 String methodName = methodDefClassInfo.methodNames[methodNo]; |
| 612 String methodSig = methodDefClassInfo.methodSignatures[methodNo]; |
| 613 for (int i = 0; i < cpoolRefsToMethodNames.length; i++) { |
| 614 if (methodName.equals(cpoolRefsToMethodNames[i]) && |
| 615 methodSig.equals(cpoolRefsToMethodSignatures[i])) { |
| 616 if (methodDefClassName.equals(cpoolRefsToMethodClasses[i])) { |
| 617 return true; // "real" reference |
| 618 } else { // Check if this is a "fake" reference that resolves t
o the above "real" reference |
| 619 // Be careful - class in the cpool may be not a project clas
s (e.g. a core class). |
| 620 ClassInfo classInThisCpool = |
| 621 pcdm.getClassInfoForName(verCode, cpoolRefsToMethodC
lasses[i]); |
| 622 if (classInThisCpool == null) { |
| 623 continue; // Class not in project |
| 624 } |
| 625 if (classInThisCpool.isSubclassOf(methodDefClassInfo.name, f
alse)) { |
| 626 // Ok, now check that this method is not actually redecl
ared in classInThisCpool (which is |
| 627 // lower in the hierarchy) or somewhere in between it an
d classInThisCpool |
| 628 boolean redeclared = false; |
| 629 ClassInfo curClass = classInThisCpool; |
| 630 do { |
| 631 if (curClass.declaresMethod(methodDefClassInfo, meth
odNo)) { |
| 632 redeclared = true; |
| 633 break; |
| 634 } |
| 635 String superName = curClass.superName; |
| 636 curClass = |
| 637 pcdm.getClassInfoForName(verCode, superName)
; |
| 638 if (curClass == null) { |
| 639 break; |
| 640 } |
| 641 } while (curClass != methodDefClassInfo); |
| 642 if (!redeclared) { |
| 643 return true; |
| 644 } |
| 645 } else if (methodDefClassInfo.isInterface() && classInThisCp
ool.implementsIntfOrSubintfDirectly(methodDefClassName)) { |
| 646 return true; |
| 647 } |
| 648 } |
| 649 } |
| 650 } |
| 651 return false; |
| 652 } |
| 653 |
| 654 /** |
| 655 * If this class has a method that throws the given exception, return its in
dex. Otherwise return -1. |
| 656 * The search starts from method with index startMethodIdx. |
| 657 */ |
| 658 public int hasMethodThrowingException(ClassInfo excClassInfo, int startMetho
dIdx) { |
| 659 if (checkedExceptions == null) { |
| 660 return -1; |
| 661 } |
| 662 if (startMethodIdx >= checkedExceptions.length) { |
| 663 return -1; |
| 664 } |
| 665 String excName = excClassInfo.name; |
| 666 for (int i = startMethodIdx; i < checkedExceptions.length; i++) { |
| 667 if (checkedExceptions[i] == null) { |
| 668 continue; |
| 669 } |
| 670 String[] exc = checkedExceptions[i]; |
| 671 for (int j = 0; j < exc.length; j++) { |
| 672 if (exc[j].equals(excName)) { |
| 673 return i; |
| 674 } |
| 675 } |
| 676 } |
| 677 return -1; |
| 678 } |
| 679 |
| 680 public static abstract class MethodHandler { |
| 681 |
| 682 abstract void handleMethod(ClassInfo ci, int methodIdx); |
| 683 } |
| 684 |
| 685 /** |
| 686 * Check this class and all its superclasses (if includeSuperclasses == true
) and superinterfaces (if includeInterfaces == true) |
| 687 * for a method with the given name. If such a method is found, call h.handl
eMethod(classInfo, methodIdx). |
| 688 */ |
| 689 public void findExistingSameNameMethods(String methodName, boolean includeSu
perclasses, boolean includeInterfaces, MethodHandler h) { |
| 690 String className = name; |
| 691 ClassInfo classInfo; |
| 692 while (className != null) { |
| 693 classInfo = pcdm.getClassInfoForName(verCode, className); |
| 694 if (classInfo == null) { |
| 695 break; // Class not in project |
| 696 } |
| 697 String mNames[] = classInfo.methodNames; |
| 698 int mNamesLen = mNames != null ? mNames.length : 0; |
| 699 for (int i = 0; i < mNamesLen; i++) { |
| 700 if (methodName.equals(mNames[i])) { |
| 701 h.handleMethod(classInfo, i); |
| 702 } |
| 703 } |
| 704 if (includeInterfaces && classInfo.interfaces != null) { |
| 705 String intfNames[] = classInfo.interfaces; |
| 706 for (int i = 0; i < intfNames.length; i++) { |
| 707 ClassInfo superIntfInfo = |
| 708 pcdm.getClassInfoForName(verCode, intfNames[i]); |
| 709 if (superIntfInfo == null) { |
| 710 continue; // Class not in project |
| 711 } |
| 712 superIntfInfo.findExistingSameNameMethods(methodName, true,
includeInterfaces, h); |
| 713 } |
| 714 } |
| 715 if (includeSuperclasses) { |
| 716 className = classInfo.superName; |
| 717 } else { |
| 718 return; |
| 719 } |
| 720 } |
| 721 } |
| 722 |
| 723 public static boolean isPrimitiveFieldSig(String fieldSig) { |
| 724 return fieldSig.indexOf('@') == -1; |
| 725 } |
| 726 |
| 727 /** |
| 728 * Check if the given signature is of a class type, and that class does not
belong to the project. |
| 729 * It used to be a check for just a core type name, but sometimes people use
JDK sources as e.g. a test |
| 730 * case - so better perform a universal (and entirely correct, unlike just a
core type name) test here. |
| 731 */ |
| 732 public boolean isNonProjectClassTypeFieldSig(String fieldSig) { |
| 733 int stPos = fieldSig.indexOf('@'); |
| 734 if (stPos == -1) { |
| 735 return false; |
| 736 } |
| 737 int endPos = fieldSig.indexOf('#'); |
| 738 String className = fieldSig.substring(stPos + 1, endPos); |
| 739 return (!pcdm.isProjectClass(verCode, className)); |
| 740 } |
| 741 |
| 742 /** For debugging. */ |
| 743 public String toString() { |
| 744 return name + (verCode == VER_OLD ? " OLD" : " NEW"); |
| 745 } |
| 746 } |
OLD | NEW |