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.lang.reflect.Modifier; |
| 10 |
| 11 |
| 12 /** |
| 13 * This class implements reading a byte array representing a class file and conv
erting it into ClassInfo. |
| 14 * |
| 15 * @author Misha Dmitriev |
| 16 * 2 March 2005 |
| 17 */ |
| 18 public class ClassFileReader extends BinaryFileReader { |
| 19 |
| 20 public static final int JAVA_MAGIC = -889275714; // 0xCAFEBABE |
| 21 public static final int JAVA_MINOR_VERSION = 0; |
| 22 public static final int JAVA_MIN_MAJOR_VERSION = 45; |
| 23 public static final int JAVA_MIN_MINOR_VERSION = 3; |
| 24 public static final int DEFAULT_MAJOR_VERSION = 46; |
| 25 public static final int DEFAULT_MINOR_VERSION = 0; |
| 26 public static final int JDK14_MAJOR_VERSION = 48; |
| 27 public static final int JDK15_MAJOR_VERSION = 49; |
| 28 public static final int JDK16_MAJOR_VERSION = 50; |
| 29 public static final int JDK17_MAJOR_VERSION = 51; |
| 30 public static final int JDK18_MAJOR_VERSION = 52; |
| 31 public static final int CONSTANT_Utf8 = 1; |
| 32 public static final int CONSTANT_Unicode = 2; |
| 33 public static final int CONSTANT_Integer = 3; |
| 34 public static final int CONSTANT_Float = 4; |
| 35 public static final int CONSTANT_Long = 5; |
| 36 public static final int CONSTANT_Double = 6; |
| 37 public static final int CONSTANT_Class = 7; |
| 38 public static final int CONSTANT_String = 8; |
| 39 public static final int CONSTANT_Fieldref = 9; |
| 40 public static final int CONSTANT_Methodref = 10; |
| 41 public static final int CONSTANT_InterfaceMethodref = 11; |
| 42 public static final int CONSTANT_NameandType = 12; |
| 43 public static final int CONSTANT_MethodHandle = 15; |
| 44 public static final int CONSTANT_MethodType = 16; |
| 45 public static final int CONSTANT_InvokeDynamic = 18; |
| 46 private ClassInfo classInfo = null; |
| 47 private int cpOffsets[]; |
| 48 private Object cpObjectCache[]; |
| 49 private byte cpTags[]; |
| 50 |
| 51 public void readClassFile(byte[] classFile, ClassInfo classInfo, String clas
sFileFullPath, boolean readFullInfo) { |
| 52 initBuf(classFile, classFileFullPath); |
| 53 this.classInfo = classInfo; |
| 54 |
| 55 readPreamble(); |
| 56 readConstantPool(readFullInfo); |
| 57 readIntermediate(); |
| 58 if (readFullInfo) { |
| 59 readFields(); |
| 60 readMethods(); |
| 61 readAttributes(); |
| 62 } |
| 63 } |
| 64 |
| 65 private int versionWord(int major, int minor) { |
| 66 return major * 1000 + minor; |
| 67 } |
| 68 |
| 69 private void readPreamble() { |
| 70 int magic = nextInt(); |
| 71 if (magic != JAVA_MAGIC) { |
| 72 throw classFileParseException("Illegal start of class file"); |
| 73 } |
| 74 int minorVersion = nextChar(); |
| 75 int majorVersion = nextChar(); |
| 76 if (majorVersion > JDK14_MAJOR_VERSION || |
| 77 versionWord(majorVersion, minorVersion) < |
| 78 versionWord(JAVA_MIN_MAJOR_VERSION, JAVA_MIN_MINOR_VERSION) ) { |
| 79 if (majorVersion == JDK18_MAJOR_VERSION) { |
| 80 classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_18; |
| 81 } else if (majorVersion == JDK17_MAJOR_VERSION) { |
| 82 classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_17; |
| 83 } else if (majorVersion == JDK16_MAJOR_VERSION) { |
| 84 classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_16; |
| 85 } else if (majorVersion == JDK15_MAJOR_VERSION) { |
| 86 classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_15; |
| 87 } else { |
| 88 throw classFileParseException("Wrong version: " + majorVersion +
"." + minorVersion); |
| 89 } |
| 90 } else { |
| 91 classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_OLDEST; |
| 92 } |
| 93 } |
| 94 |
| 95 private void readConstantPool(boolean readFullInfo) { |
| 96 int classRefsNo = 0; |
| 97 int fieldRefsNo = 0; |
| 98 int methodRefsNo = 0; |
| 99 |
| 100 cpOffsets = new int[nextChar()]; |
| 101 cpTags = new byte[cpOffsets.length]; |
| 102 int ofs, len, classIdx, nameAndTypeIdx, nameIdx, sigIdx, utf8Idx; |
| 103 int i = 1; |
| 104 while (i < cpOffsets.length) { |
| 105 byte tag = buf[curBufPos++]; |
| 106 cpOffsets[i] = curBufPos; |
| 107 cpTags[i] = tag; |
| 108 i++; |
| 109 switch (tag) { |
| 110 case CONSTANT_Utf8: |
| 111 len = nextChar(); |
| 112 curBufPos += len; |
| 113 break; |
| 114 |
| 115 case CONSTANT_Class: |
| 116 classRefsNo++; |
| 117 curBufPos += 2; |
| 118 break; |
| 119 |
| 120 case CONSTANT_String: |
| 121 case CONSTANT_MethodType: |
| 122 curBufPos += 2; |
| 123 break; |
| 124 |
| 125 case CONSTANT_Fieldref: |
| 126 fieldRefsNo++; |
| 127 curBufPos += 4; |
| 128 break; |
| 129 |
| 130 case CONSTANT_Methodref: |
| 131 case CONSTANT_InterfaceMethodref: |
| 132 methodRefsNo++; |
| 133 curBufPos += 4; |
| 134 break; |
| 135 |
| 136 case CONSTANT_MethodHandle: |
| 137 curBufPos += 3; |
| 138 break; |
| 139 |
| 140 case CONSTANT_NameandType: |
| 141 case CONSTANT_Integer: |
| 142 case CONSTANT_Float: |
| 143 case CONSTANT_InvokeDynamic: |
| 144 curBufPos += 4; |
| 145 break; |
| 146 |
| 147 case CONSTANT_Long: |
| 148 case CONSTANT_Double: |
| 149 curBufPos += 8; |
| 150 i++; |
| 151 break; |
| 152 |
| 153 default: |
| 154 throw classFileParseException("Bad constant pool tag: " + ta
g + " at " + Integer.toString(curBufPos - 1)); |
| 155 } |
| 156 } |
| 157 |
| 158 cpObjectCache = new Object[cpOffsets.length]; |
| 159 if (!readFullInfo) { |
| 160 return; |
| 161 } |
| 162 |
| 163 classInfo.cpoolRefsToClasses = new String[classRefsNo]; |
| 164 classInfo.isRefClassArray = new boolean[classRefsNo]; |
| 165 classInfo.cpoolRefsToFieldClasses = new String[fieldRefsNo]; |
| 166 classInfo.cpoolRefsToFieldNames = new String[fieldRefsNo]; |
| 167 classInfo.cpoolRefsToFieldSignatures = new String[fieldRefsNo]; |
| 168 classInfo.cpoolRefsToMethodClasses = new String[methodRefsNo]; |
| 169 classInfo.cpoolRefsToMethodNames = new String[methodRefsNo]; |
| 170 classInfo.cpoolRefsToMethodSignatures = new String[methodRefsNo]; |
| 171 |
| 172 int curClassRef = 0; |
| 173 int curFieldRef = 0; |
| 174 int curMethodRef = 0; |
| 175 |
| 176 for (i = 0; i < cpOffsets.length; i++) { |
| 177 ofs = cpOffsets[i]; |
| 178 switch (cpTags[i]) { |
| 179 case CONSTANT_Class: |
| 180 utf8Idx = getChar(ofs); |
| 181 classInfo.cpoolRefsToClasses[curClassRef++] = |
| 182 classNameAtCPIndex(utf8Idx, classInfo.isRefClassArra
y, curClassRef - 1); |
| 183 //System.out.println("Read cpool ref to class: " + classInfo
.cpoolRefsToClasses[curClassRef-1]); |
| 184 break; |
| 185 |
| 186 case CONSTANT_Fieldref: |
| 187 classIdx = getChar(ofs); |
| 188 nameAndTypeIdx = getChar(ofs + 2); |
| 189 if (cpTags[classIdx] != CONSTANT_Class || cpTags[nameAndType
Idx] != CONSTANT_NameandType) { |
| 190 badCPReference(ofs, i); |
| 191 } |
| 192 classInfo.cpoolRefsToFieldClasses[curFieldRef] = |
| 193 classNameAtCPIndex(getChar(cpOffsets[classIdx])); |
| 194 |
| 195 ofs = cpOffsets[nameAndTypeIdx]; |
| 196 nameIdx = getChar(ofs); |
| 197 sigIdx = getChar(ofs + 2); |
| 198 if (cpTags[nameIdx] != CONSTANT_Utf8 || cpTags[sigIdx] != CO
NSTANT_Utf8) { |
| 199 badCPReference(ofs, i); |
| 200 } |
| 201 classInfo.cpoolRefsToFieldNames[curFieldRef] = |
| 202 utf8AtCPIndex(nameIdx); |
| 203 classInfo.cpoolRefsToFieldSignatures[curFieldRef] = |
| 204 signatureAtCPIndex(sigIdx); |
| 205 //System.out.println("Read cpool ref to field: " + classInfo
.cpoolRefsToFieldNames[curFieldRef] + " " + |
| 206 // classInfo.cpoolRefsToFieldSignatures[cu
rFieldRef]); |
| 207 curFieldRef++; |
| 208 break; |
| 209 |
| 210 case CONSTANT_Methodref: |
| 211 case CONSTANT_InterfaceMethodref: |
| 212 classIdx = getChar(ofs); |
| 213 nameAndTypeIdx = getChar(ofs + 2); |
| 214 if (cpTags[classIdx] != CONSTANT_Class || cpTags[nameAndType
Idx] != CONSTANT_NameandType) { |
| 215 badCPReference(ofs, i); |
| 216 } |
| 217 classInfo.cpoolRefsToMethodClasses[curMethodRef] = |
| 218 classNameAtCPIndex(getChar(cpOffsets[classIdx])); |
| 219 |
| 220 ofs = cpOffsets[nameAndTypeIdx]; |
| 221 nameIdx = getChar(ofs); |
| 222 sigIdx = getChar(ofs + 2); |
| 223 if (cpTags[nameIdx] != CONSTANT_Utf8 || cpTags[sigIdx] != CO
NSTANT_Utf8) { |
| 224 badCPReference(ofs, i); |
| 225 } |
| 226 classInfo.cpoolRefsToMethodNames[curMethodRef] = |
| 227 utf8AtCPIndex(nameIdx); |
| 228 classInfo.cpoolRefsToMethodSignatures[curMethodRef] = |
| 229 signatureAtCPIndex(sigIdx); |
| 230 //System.out.println("Read cpool ref to method: " + classInf
o.cpoolRefsToMethodNames[curMethodRef] + " " + |
| 231 // classInfo.cpoolRefsToMethodSignatures[c
urMethodRef]); |
| 232 curMethodRef++; |
| 233 break; |
| 234 } |
| 235 } |
| 236 } |
| 237 |
| 238 private void readIntermediate() { |
| 239 int i, classIdx, superClassIdx; |
| 240 |
| 241 classInfo.accessFlags = nextChar(); |
| 242 classIdx = nextChar(); |
| 243 if (cpTags[classIdx] != CONSTANT_Class) { |
| 244 throw classFileParseException("Bad reference to this class name"); |
| 245 } |
| 246 classInfo.name = classNameAtCPIndex(getChar(cpOffsets[classIdx])); |
| 247 superClassIdx = nextChar(); |
| 248 if (!"java/lang/Object".equals(classInfo.name)) { |
| 249 if (cpTags[superClassIdx] != CONSTANT_Class) { |
| 250 throw classFileParseException("Bad reference to super class name
"); |
| 251 } |
| 252 classInfo.superName = |
| 253 classNameAtCPIndex(getChar(cpOffsets[superClassIdx])); |
| 254 } |
| 255 |
| 256 char intfCount = nextChar(); |
| 257 if (intfCount != 0) { |
| 258 classInfo.interfaces = new String[intfCount]; |
| 259 for (i = 0; i < intfCount; i++) { |
| 260 classIdx = nextChar(); |
| 261 if (cpTags[classIdx] != CONSTANT_Class) { |
| 262 throw classFileParseException("Bad reference to an implement
ed interface"); |
| 263 } |
| 264 classInfo.interfaces[i] = |
| 265 classNameAtCPIndex(getChar(cpOffsets[classIdx])); |
| 266 } |
| 267 } |
| 268 } |
| 269 |
| 270 private void readFields() { |
| 271 int i, j; |
| 272 |
| 273 char definedFieldCount = nextChar(); |
| 274 if (definedFieldCount == 0) { |
| 275 return; |
| 276 } |
| 277 |
| 278 String names[] = new String[definedFieldCount]; |
| 279 String signatures[] = new String[definedFieldCount]; |
| 280 char accessFlags[] = new char[definedFieldCount]; |
| 281 |
| 282 // We are not going to record information on private fields which have e
ither primitive or non-project-class |
| 283 // (typically core-class) types. Such fields cannot affect anything exce
pt their own class, so we don't need them. |
| 284 int ri = 0; |
| 285 |
| 286 for (i = 0; i < definedFieldCount; i++) { |
| 287 char flags = nextChar(); |
| 288 String name = utf8AtCPIndex(nextChar()); |
| 289 String sig = signatureAtCPIndex(nextChar()); |
| 290 |
| 291 boolean recordField = |
| 292 !(Modifier.isPrivate(flags) && |
| 293 (ClassInfo.isPrimitiveFieldSig(sig) || classInfo.isNonProjec
tClassTypeFieldSig(sig))); |
| 294 |
| 295 int attrCount = nextChar(); |
| 296 for (j = 0; j < attrCount; j++) { |
| 297 int attrNameIdx = nextChar(); |
| 298 int attrLen = nextInt(); |
| 299 if (recordField && utf8AtCPIndex(attrNameIdx).equals("ConstantVa
lue") && |
| 300 Modifier.isFinal(flags)) { |
| 301 if (classInfo.primitiveConstantInitValues == null) { |
| 302 classInfo.primitiveConstantInitValues = |
| 303 new Object[definedFieldCount]; |
| 304 } |
| 305 int constValueIdx = nextChar(); |
| 306 switch (cpTags[constValueIdx]) { |
| 307 case CONSTANT_String: |
| 308 classInfo.primitiveConstantInitValues[ri] = |
| 309 utf8AtCPIndex(getChar(cpOffsets[constValueId
x])); |
| 310 break; |
| 311 |
| 312 case CONSTANT_Integer: |
| 313 classInfo.primitiveConstantInitValues[ri] = |
| 314 Integer.valueOf(getInt(cpOffsets[constValueI
dx])); |
| 315 break; |
| 316 |
| 317 case CONSTANT_Long: |
| 318 classInfo.primitiveConstantInitValues[ri] = |
| 319 Long.valueOf(getLong(cpOffsets[constValueIdx
])); |
| 320 break; |
| 321 |
| 322 case CONSTANT_Float: |
| 323 classInfo.primitiveConstantInitValues[ri] = |
| 324 Float.valueOf(getFloat(cpOffsets[constValueI
dx])); |
| 325 break; |
| 326 |
| 327 case CONSTANT_Double: |
| 328 classInfo.primitiveConstantInitValues[ri] = |
| 329 Double.valueOf(getDouble(cpOffsets[constValu
eIdx])); |
| 330 break; |
| 331 |
| 332 default: |
| 333 badCPEntry(constValueIdx); |
| 334 } |
| 335 |
| 336 } else { |
| 337 curBufPos += attrLen; |
| 338 } |
| 339 } |
| 340 |
| 341 if (recordField) { |
| 342 names[ri] = name; |
| 343 signatures[ri] = sig; |
| 344 accessFlags[ri] = flags; |
| 345 ri++; |
| 346 } |
| 347 } |
| 348 |
| 349 if (ri == definedFieldCount) { |
| 350 classInfo.fieldNames = names; |
| 351 classInfo.fieldSignatures = signatures; |
| 352 classInfo.fieldAccessFlags = accessFlags; |
| 353 } else if (ri > 0) { |
| 354 classInfo.fieldNames = new String[ri]; |
| 355 classInfo.fieldSignatures = new String[ri]; |
| 356 classInfo.fieldAccessFlags = new char[ri]; |
| 357 System.arraycopy(names, 0, classInfo.fieldNames, 0, ri); |
| 358 System.arraycopy(signatures, 0, classInfo.fieldSignatures, 0, ri); |
| 359 System.arraycopy(accessFlags, 0, classInfo.fieldAccessFlags, 0, ri); |
| 360 } |
| 361 } |
| 362 |
| 363 private void readMethods() { |
| 364 int i, j; |
| 365 |
| 366 char methodCount = nextChar(); |
| 367 if (methodCount == 0) { |
| 368 return; |
| 369 } |
| 370 |
| 371 String names[] = new String[methodCount]; |
| 372 String signatures[] = new String[methodCount]; |
| 373 char accessFlags[] = new char[methodCount]; |
| 374 |
| 375 for (i = 0; i < methodCount; i++) { |
| 376 accessFlags[i] = nextChar(); |
| 377 names[i] = utf8AtCPIndex(nextChar()); |
| 378 signatures[i] = signatureAtCPIndex(nextChar()); |
| 379 |
| 380 int attrCount = nextChar(); |
| 381 for (j = 0; j < attrCount; j++) { |
| 382 int attrNameIdx = nextChar(); |
| 383 int attrLen = nextInt(); |
| 384 if (utf8AtCPIndex(attrNameIdx).equals("Exceptions")) { |
| 385 if (classInfo.checkedExceptions == null) { |
| 386 classInfo.checkedExceptions = new String[methodCount][]; |
| 387 } |
| 388 int nExceptions = nextChar(); |
| 389 String exceptions[] = new String[nExceptions]; |
| 390 for (int k = 0; k < nExceptions; k++) { |
| 391 int excClassIdx = nextChar(); |
| 392 if (cpTags[excClassIdx] != CONSTANT_Class) { |
| 393 badCPEntry(excClassIdx); |
| 394 } |
| 395 exceptions[k] = |
| 396 classNameAtCPIndex(getChar(cpOffsets[excClassIdx
])); |
| 397 } |
| 398 classInfo.checkedExceptions[i] = exceptions; |
| 399 } else { |
| 400 curBufPos += attrLen; |
| 401 } |
| 402 } |
| 403 } |
| 404 |
| 405 classInfo.methodNames = names; |
| 406 classInfo.methodSignatures = signatures; |
| 407 classInfo.methodAccessFlags = accessFlags; |
| 408 } |
| 409 |
| 410 /** |
| 411 * This method actually reads only the information related to the nested cla
sses, and |
| 412 * records only those of them which are first level nested classes of this c
lass. The class |
| 413 * may also reference other classes which are not package members through th
e same |
| 414 * InnerClasses attribute - their names would be processed when their respec
tive enclosing |
| 415 * classes are read. |
| 416 */ |
| 417 private void readAttributes() { |
| 418 String nestedClassPrefix = classInfo.name + "$"; |
| 419 |
| 420 char attrCount = nextChar(); |
| 421 |
| 422 for (int i = 0; i < attrCount; i++) { |
| 423 int attrNameIdx = nextChar(); |
| 424 int attrLen = nextInt(); |
| 425 if (utf8AtCPIndex(attrNameIdx).equals("InnerClasses")) { |
| 426 int nOfClasses = nextChar(); |
| 427 String nestedClasses[] = new String[nOfClasses]; |
| 428 char nestedClassAccessFlags[] = new char[nOfClasses]; |
| 429 boolean nestedClassNonMember[] = new boolean[nOfClasses]; |
| 430 int curIdx = 0; |
| 431 for (int j = 0; j < nOfClasses; j++) { |
| 432 int innerClassInfoIdx = nextChar(); |
| 433 int outerClassInfoIdx = nextChar(); |
| 434 int innerClassNameIdx = nextChar(); |
| 435 char innerClassAccessFlags = nextChar(); |
| 436 |
| 437 // Even if a class is private or non-member (innerClassAcces
sFlags has private bit set or |
| 438 // outerClassInfoIdx == 0), we still should take this class
into account, since it may e.g. extend |
| 439 // a public class/implement a public interface, which, in tu
rn, may be changed incompatibly. |
| 440 |
| 441 String nestedClassFullName = classNameAtCPIndex(getChar(cpOf
fsets[innerClassInfoIdx])); |
| 442 |
| 443 // We are only interested the nested classes whose enclosing
class is this one. |
| 444 if (!nestedClassFullName.startsWith(nestedClassPrefix)) |
| 445 continue; |
| 446 |
| 447 // We are only interested in the directly nested classes of
this class. |
| 448 String nestedClassNameSuffix = nestedClassFullName.substring
(nestedClassPrefix.length()); |
| 449 |
| 450 if (innerClassNameIdx == 0) { |
| 451 // Nested class is anonymous. Suffix must be all digits. |
| 452 if (findFirstNonDigit(nestedClassNameSuffix) != -1) |
| 453 continue; |
| 454 } else { |
| 455 // Nested class is named. |
| 456 String nestedClassSimpleName = utf8AtCPIndex(innerClassN
ameIdx); |
| 457 // The simple case is Outer$Inner. |
| 458 if (!nestedClassNameSuffix.equals(nestedClassSimpleName)
) { |
| 459 // The more complicated case is a local class. In JD
K 1.5+ These are named, |
| 460 // e.g., Outer$1Inner. Pre-JDK 1.5 they are named e.
g., Outer$1$Inner. |
| 461 int p = findFirstNonDigit(nestedClassNameSuffix); |
| 462 if (p == -1) |
| 463 continue; |
| 464 if (classInfo.javacTargetRelease == Utils.JAVAC_TARG
ET_RELEASE_OLDEST && |
| 465 nestedClassNameSuffix.charAt(p++) != '$') |
| 466 continue; |
| 467 if (!nestedClassNameSuffix.substring(p).equals(neste
dClassSimpleName)) |
| 468 continue; |
| 469 } |
| 470 } |
| 471 |
| 472 // The name has passed all checks, so register it. |
| 473 |
| 474 nestedClasses[curIdx] = nestedClassFullName; |
| 475 nestedClassAccessFlags[curIdx] = innerClassAccessFlags; |
| 476 nestedClassNonMember[curIdx] = (outerClassInfoIdx == 0); |
| 477 curIdx++; |
| 478 } |
| 479 if (curIdx == nOfClasses) { |
| 480 classInfo.nestedClasses = nestedClasses; |
| 481 classInfo.nestedClassAccessFlags = nestedClassAccessFlags; |
| 482 classInfo.nestedClassNonMember = nestedClassNonMember; |
| 483 } else if (curIdx > 0) { |
| 484 // We found fewer nested classes for this class than we orig
inally expected, but still more than 0. |
| 485 // Create a new array to fit their number exactly. |
| 486 classInfo.nestedClasses = new String[curIdx]; |
| 487 classInfo.nestedClassAccessFlags = new char[curIdx]; |
| 488 classInfo.nestedClassNonMember = new boolean[curIdx]; |
| 489 System.arraycopy(nestedClasses, 0, classInfo.nestedClasses,
0, curIdx); |
| 490 System.arraycopy(nestedClassAccessFlags, 0, classInfo.nested
ClassAccessFlags, 0, curIdx); |
| 491 System.arraycopy(nestedClassNonMember, 0, classInfo.nestedCl
assNonMember, 0, curIdx); |
| 492 } |
| 493 } else { |
| 494 curBufPos += attrLen; |
| 495 } |
| 496 } |
| 497 } |
| 498 |
| 499 private int findFirstNonDigit(String s) { |
| 500 for (int i = 0; i < s.length(); i++) { |
| 501 if (!Character.isDigit(s.charAt(i))) |
| 502 return i; |
| 503 } |
| 504 return -1; |
| 505 } |
| 506 |
| 507 private String utf8AtCPIndex(int idx) { |
| 508 if (cpTags[idx] != CONSTANT_Utf8) { |
| 509 throw classFileParseException("Constant pool entry " + idx + " shoul
d be UTF8 constant"); |
| 510 } |
| 511 if (cpObjectCache[idx] == null) { |
| 512 int utf8Len = getChar(cpOffsets[idx]); |
| 513 // String interning reduces the size of the disk database very signi
ficantly |
| 514 // (by one-third in one observed case), and also speeds up database
search. |
| 515 cpObjectCache[idx] = |
| 516 (new String(buf, cpOffsets[idx] + 2, utf8Len)).intern(); |
| 517 } |
| 518 return (String) cpObjectCache[idx]; |
| 519 } |
| 520 |
| 521 private String classNameAtCPIndex(int idx) { |
| 522 return classNameAtCPIndex(idx, null, 0); |
| 523 } |
| 524 |
| 525 /** |
| 526 * Read class name at the given CONSTANT_Utf8 constant pool index, and retur
n it |
| 527 * trimmed of the possible '[' and 'L' prefixes and the ';' suffix. |
| 528 */ |
| 529 private String classNameAtCPIndex(int idx, boolean isRefClassArray[], int is
ArrayIdx) { |
| 530 if (cpTags[idx] != CONSTANT_Utf8) { |
| 531 throw classFileParseException("Constant pool entry " + idx + " shoul
d be UTF8 constant"); |
| 532 } |
| 533 boolean isArray = false; |
| 534 if (cpObjectCache[idx] == null) { |
| 535 int utf8Len = getChar(cpOffsets[idx]); |
| 536 int stPos = cpOffsets[idx] + 2; |
| 537 int initStPos = stPos; |
| 538 while (buf[stPos] == '[') { |
| 539 stPos++; |
| 540 } |
| 541 if (stPos != initStPos) { |
| 542 isArray = true; |
| 543 if (buf[stPos] == 'L') { |
| 544 stPos++; |
| 545 utf8Len--; // To get rid of the terminating ';' |
| 546 } |
| 547 } |
| 548 utf8Len = utf8Len - (stPos - initStPos); |
| 549 cpObjectCache[idx] = (new String(buf, stPos, utf8Len)).intern(); |
| 550 if (isRefClassArray != null) { |
| 551 isRefClassArray[isArrayIdx] = isArray; |
| 552 } |
| 553 } |
| 554 return (String) cpObjectCache[idx]; |
| 555 } |
| 556 |
| 557 // We replace all "Lclassname;" in signatures with "@classname#" to simplify
signature parsing during reference checking |
| 558 private String signatureAtCPIndex(int idx) { |
| 559 if (cpTags[idx] != CONSTANT_Utf8) { |
| 560 throw classFileParseException("Constant pool entry " + idx + " shoul
d be UTF8 constant"); |
| 561 } |
| 562 if (cpObjectCache[idx] == null) { |
| 563 int utf8Len = getChar(cpOffsets[idx]); |
| 564 byte tmp[] = new byte[utf8Len]; |
| 565 System.arraycopy(buf, cpOffsets[idx] + 2, tmp, 0, utf8Len); |
| 566 boolean inClassName = false; |
| 567 for (int i = 0; i < utf8Len; i++) { |
| 568 if (!inClassName) { |
| 569 if (tmp[i] == 'L') { |
| 570 tmp[i] = '@'; |
| 571 inClassName = true; |
| 572 } |
| 573 } else if (tmp[i] == ';') { |
| 574 tmp[i] = '#'; |
| 575 inClassName = false; |
| 576 } |
| 577 } |
| 578 cpObjectCache[idx] = (new String(tmp)).intern(); |
| 579 } |
| 580 return (String) cpObjectCache[idx]; |
| 581 } |
| 582 |
| 583 private void badCPReference(int ofs, int i) { |
| 584 throw classFileParseException("Bad constant pool reference: " + ofs + "
from entry " + i); |
| 585 } |
| 586 |
| 587 private void badCPEntry(int entryNo) { |
| 588 throw classFileParseException("Constant pool entry " + entryNo + " : inv
alid type"); |
| 589 } |
| 590 |
| 591 private PrivateException classFileParseException(String msg) { |
| 592 return new PrivateException(new PublicExceptions.ClassFileParseException
( |
| 593 "Error reading class file " + fileFullPath + ":\n" + msg)); |
| 594 } |
| 595 } |
OLD | NEW |